Go to comments

JavaScript 命名空间

一、命名空间


命名空间的问题是些小散碎的知识点,是用以前的知识点解决新的问题。

命名空间问题,是企业开发的一个实际的困扰性问题,比如开发一个淘宝网首页都是很多个人来做,你做导航栏、我做输入框、他做下面商品展示,下面的页脚链接,等等一些列的再做整体渲染。

一个淘宝事业部大概两千多人,有很多人去做就涉及一个问题,大家做完的东西得拼到一起,因为是一个HTML页面,拼到一起就会发生很多的冲突,HTML的冲突、CSS的冲突都是次要的。


我们先讲JavaScript的冲突,比如你写的一块有个功能,我写的一块也有功能,把咱们两的功能拼到一起,虽然写的文件是你一个js文档、我一个js文档,但最后总要落到一个HTML文件里去吧,都要一个HTML文档往里引入很多了js。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
    <script src="Glee.js"></script>
    <script src="LiLi.js"></script>
    <script src="Fang.js"></script>
</head>
<body>
</body>
</html>

引入很多js不是操作一个东西,比如我写了一小块html,我的js就是操作我的HTML的,你写的一点就操作你的,再加上基础数据、基础变量、数组什么的,一堆js拼到一个页面里面去,大家的代码是这样去整合的。

大家把这些script标签放的一个页面里面,放到一个页面里有个问题,比如我命名一个变量a写在全局,他也写了一个变量名字也是a

var a = 100;
var a = 200;

alert(a);

还没等用到我变量就给覆盖了,两个人的变量就有冲突了。


大家在公司里都是经过专业入职培训的,培训完结果是大家代码格式、语法变量用的都非常标准(精准),比如说一个广告的功能大家都用变量基本都是ad,导航栏功能都用nav(Navigation),所以同样的一个功能用的变量名基本是一致的,保持高度一致好维护,因为规范大家都这么写。

保持一致就好玩了,比如你功能里面可能和我的功能里面东西比较像,我要实现功能定义这个变量,你要实现功能也定义这个变量,单词一共就那么几个,重复率就非常高。


大家一起协调配合时没法配合,变量都重复了怎么办?后来想一个笨方法,在个人变量前面加上自己名字的缩写。

比如定义一个变量name,加上自己的名字YanYanDiFangName,这样重复的概率就少了!有人名子起的飘逸点四个字,所以名字这个并不靠谱。

var YanYanDiFangName = "远远的地方";

再后来衍生了一个新的东西叫"命名空间"。

1. 命名空间

大家写代码的时候规定一个命名空间,把变量全放到命名空间里面,这个命名空间名字很高大上,其实内容都学过的就是对象。

比如定义一个对象叫org,给对象起一个名字org,对象起到什么作用呢?

1). 这个对象org在全局主域,org是总js里面的一个对象。对象里面可以有对象,属性值是随意的。

2). 我以后用的变量就在对象里面写,在里面定义一些列我想要的变量和方法,都定义到对象里面。

这样的话至少能保障,命名的不冲突,而且能非常的规整化,谁在部门1(department1)里面的谁下面有什么样的变量,部门2(department2)里面的谁下面有什么样的变量。

var org = {
    department1 : {
        YiEn : {
            name : "abc",
            age : 123
        },
        LiLi : {

        }
    },
    department2 : {
        Glee : {

        },
        FangJing : {

        }
    }
}

console.log(org.department1.YiEn.name); // 用的时候这样用

看着难受,用的时候也难受,能不能简单点!

又给简化了一下,我想用自己变量的时候,先定义一个变量YiEn,把org.department1.YiEn这个引用赋给YiEn,引用值赋值给一个短语是可以的,这样YiEn就相当于我的空间了。

var org = {
    department1 : {
        YiEn : {
            name : "abc",
            age : 123
        },
        LiLi : {

        }
    },
    department2 : {
        Glee : {

        },
        FangJing : {

        }
    }
}

var YiEn = org.department1.YiEn;

console.log(YiEn.name); // 这样用就简单点了。

这个方法看一眼(知道)就行了,现在不这么用了,工具越来越复杂,越来越强大了根本就不需要这样了。

十年前(2007年)在解决这个问题的时候,确实用到了命名空间,也确实是这么用的,就是防止大家的命名不冲突,因为你对象里面的名字和我对象里面的名字即使一样也不冲突,"a.name"和"b.name"虽然都叫name无所谓不冲突,但在全局范围命名内两个name就会覆盖了。

这是老办法解决命名冲突的问题,这个办法一定记住叫"命名空间",就是对象模拟命名空间。现在真正解决的办法是webpack,包括很多同步化开放的软件。现在不讲这些东西,讲一个目前能接触到的,一个防止命名空间的一个问题,非常强大!

二、模块化开发,防止污染全局变量

这是一个项目包,项目包里有很多东西,component是一个组件,大家每一个人写一部分HTML,然后把HTML拼到一起,最后通过软件拼到主页面index.html里面。

主页面index.html里面"<!--online[component/news-ad.html]-->",看着是注释其实不是注释,这是后期的一个语法,把这些组件都拼到一起。


sc_topic_news_la.js这是js文件,一开始的作者是lxk他主要推进的,然后大家来配合,一个init就是一个人写的initAd、initTouPiao...

这个项目不是一个人写一个js文件,然后大家拼到一起了,而是在别人写完的js上去改东西,更直观的暴露一个问题,命名别冲突。怎么办的呢?用闭包来写。

大家没在全局上写,我通过"initAd = (function (){})"这样的方法,因为我写的是一个功能,功能等待被调用,在哪调用的呢?

在initAd()里面调用,于是initAd()功能实现的变量的调用、方法的实现,就写到一个独立的空间里面,就是闭包里面,怎么来写的?

这么来写的,变量initAd等于一个立即执行函数,这个立即执行函数执行完之后,会返回一个函数,等待被调用。

在立即执行函数里定义了一大堆变量、一大堆函数,但这些都是在立即执行函数里定义的,立即执行函数执行完之后自己就销毁了,然而想让这个功能保存到外部,等待被调用的话,保存了一个函数出来,把这一个函数保存出来之后,这一个函数会和刚才的域形成一个闭包。

然后在被保存出来的函数里面,写上方法的调用就可以了。

把这个简化一下,写一个小demo。比如要实现一个init()功能,这个功能是打印里面的属性name的值。

var name = "LiLi";

var init = (function(){

    var name = "Glee";

    function callName(){
        console.log(name);
    }

    // 1. return function函数出来之后,init就相当于这个函数了。
    // 2. 为什么这么来写
    return function(){ 
        callName(); // 3.因为callName()是为了开启init里面的功能,init里面所有的功能都是为了callName()函数的
    } // 4. "return functon (){}函数"被保存到了外面init的身上,init()就代表了callName()这个功能

}());

init(); // 5. 所以执行init(),就相当于调用立即执行函数里面的东西

console.log(name); // 6. 在外面也定义一个name变量,和立即执行函数里面的name,它俩互相不影响

自执行函数形成一个闭包,闭包能变量私有化,不会污染全局变量。不污染全局变量就可以把特定的功能,写到这样一个闭包里面去,然后留出来一个接口方便去启动它。

怎么留接口?留出来一个"return function () {}",然后这个function里面去执行立即执行里面的一个函数(callName())作为中转。因为要实现一个功能,肯定要基于函数,函数调用函数,再调用函数,然后在使用变量。

// init对象里面的这段代码就是接口

return function(){ 
    callName(); 
}

自执行函数里面定义好一系列复杂的功能,然后留出一个函数作为接口传给init。这个init被调用的时候,就会启动里面的callName()函数。

然而在这个过程中,功能被实现了函数也能被启用,但是有一个好处是,并不污染全局变量,里面定义的变量跟外面的一点关系也没有,但是功能也实现了,这是通过闭包来实现私有化的一个过程。

比如,我写的一个功能init,需要我定义一个变量,name等一系列东西。可能又有一个人又写一个功能initLiu,他也定义了一个变量name,也callName方法。

var name = "LiLi";

var init = (function(){

    var name = "Glee";

    function callName(){
        console.log(name);
    }

    return function(){ 
        callName(); 
    }

}());


var initLu = (function(){

    var name = "Yan";

    function callName(){
        console.log(name);
    }

    return function(){
        callName();
    }

}());

init(); // 打印Glee实现我功能

initDeng(); // 打印Yan实现他的功能

大家互不影响,但却各自实现了各自的功能,我的变量可以随便起名,别人的变量也可以随便起名。把在全局范围内要实现的功能,放到了一个局部里面,就是让它互相不污染,

以后要实现一个功能,这个功能是可以复用的(可以被重复使用的),以后开发中还会用到这个功能。就可以把这个功能提取到这样一个闭包里面去,再用的时候直接拷贝过来,根本不用担心变量被污染的问题。

污染全局变量的问题两种方法,老方法是命名空间(这个不用里),新方法就是用闭包,闭包是一种高端开发方法。


PS:

init是什么意思?

启到入口名字的作用,是初始化的意思。

在编程的时候,程序写的非常复杂,比如有一万行代码,想让别人读的懂是非常吃力的,因为别人不知道代码是从拿开始执行的。

为了能更好的找到入口函数,把入口函数最开始被启动的函数叫"init"。大家顺着init函数就知道,init函数里调用的谁谁,然后去找谁谁,然后依次能把函数的执行顺序读下来。init是约定俗成的一个用法,高端开发init。


然后再回过头看这个"var initAd = (function (){ })"就能理解了,立即执行函数里面用到了一大堆变量,又有一大堆函数,这些函数之间可能是互相调用、互相嵌套的。

image.png

然后不管里面是怎么样互相调用的,在return function(){}这启动,把这些addEventListener()、hideOnWeChat()、hideAd(),在return function(){}里启动起来。

这是闭包的第四点应用,模块化开发,防止污染全局变量。

三、思考问题

jquery是一个非常强大的库(复杂的文件包),这个文件包里面留出了很多方法接口,我们可以用它的方法实现自己的功能,它非常强大。

<div></div>

$("div").css('background', 'red').width(100).height(100).html('加点内容').css('position' ,'absolute').css('left' , '100px').css('top' ,'100px');

jQuery有一个非常强大的链式操作,.css()是一个方法调用、.width()是一个方法调用里面传的是参数,jQuery可以一个方法接着一个方法的调用。

我们能不能这样模拟jQuery,调完一个方法再调一个方法!

这是一个对象上有三个方法,我们能不能连续调用!

var LiLi = {
    motion : function () {
        console.log('看足球,逛街');
    },
    drink : function () {
        console.log('喝茅台!');
    },
    perm : function () {
        console.log('做头发,护肤,做美甲!');
    }
}

LiLi.motion();
LiLi.drink();
LiLi.perm();

对象上面有三个方法,现在只能这样deng.smoke(),然后是LiLi.drink(),然后是LiLi.perm()

现在想连续调用,试一下LiLi.smoke().drink()

var LiLi = {
    motion : function () {
        console.log('看足球,逛街');
        // return undefined;
    },
    drink : function () {
        console.log('喝茅台!');
    },
    perm : function () {
        console.log('做头发,护肤,做美甲!');
    }
}

LiLi.motion().drink();  // 看足球,逛街
                        // Uncaught TypeError: Cannot read property 'drink' of undefined

第一个打印出来了,第二个报错"Uncaught TypeError: Cannot read property 'drink' of undefined",意思是undefined上面没有drink方法。

为什么会打印出这样的错误,因为motion执行完后会隐式的返回undefined,所以undefined不可能有drink属性的。

就想模拟模拟jQuery连着调用,jquery是怎么连续调用的?在函数里面"return LiLi"

var LiLi = {
    motion : function () {
        console.log('看足球,逛街');
        return LiLi;
    },
    drink : function () {
        console.log('喝茅台!');
        return LiLi;
    },
    perm : function () {
        console.log('做头发,护肤,做美甲!');
        return LiLi;
    }
}

LiLi.motion().drink().perm().motion().perm();

"return LiLi"已经接近的正确答案了,有没有更好的,还能return其它东西吗?

在一个对象的函数里面,this指向的是第一人称我,this代表我"return this"更好。

var LiLi = {
    motion : function () {
        console.log('看足球,逛街');
        return this;
    },
    drink : function () {
        console.log('喝茅台!');
        return this;
    },
    perm : function () {
        console.log('做头发,护肤,做美甲!');
        return this;
    }
}

LiLi.motion().drink().perm().motion().perm();

是可以"return LiLi"但要是把这个对象的构造权,放到构造函数里面呢?不知道构造出的对象叫什么名字,每次名字都会不一样,所以只能return this。

想实现一个方法的连续调用"return this"这是一个小技巧。再学一个小知识点,属性的表示方法


Leave a comment 0 Comments.

Leave a Reply

换一张