天天看点

第四节:命名空间

当系统大了、程序复杂了、写的人多了,名字的问题就是个大问题。目前来说,解决名字问题的最好办法就是命名空间。

使用命名空间,可以避免变量或对象名称产生的冲突,同时,也有助于组织代码,有更强的可维护性和可读性。

javascript不提供原生的命名空间支持,但我们可以利用javascript的一些语言特性,实现类似的效果。

1.闭包

简单的说,闭包就是,一个函数可以使用函数之外定义的变量,那么这个函数就是闭包。

var msg = "你好,朋友";

function sayhello() {

  alert(msg);

}

sayhello();

这就是一个最简单的闭包实例。函数sayhello调用了函数外部定义的变量msg,所以函数sayhello就是闭包。

function extfunc() {

    var n = 1;

    function internal_func(){

        alert(n++);

    }

    return internal_func();

extfunc();

这个例子稍微复杂一些,其内部函数(internal_func )是一个闭包,因为它使用外部函数的变量n的值。extfunc()的最后一步调用了internal_func(),增加了变量n的值,并打印它。

通过闭包可以让函数内部变量的值始终保持在内存中。比如下面的例子,函数fn()无论执行多少次,其内部变量n的值都是1,因为函数每次执行的时候都会重新分配内存,并初始化其内部变量,而且执行完毕,会释放函数内部变量的内存。

function fn() {

    alert(n++);

fn();

现在,我们用闭包做一些处理。

    function in_fn(){

    return in_fn;

var f = fn();

f();

我们发现,每次执行,函数fn()内部的变量n的值都会加一,说明变量n的值始终保存在内存中了。

这个例子中,函数fn()最后返回了闭包函数in_fn(),也就是说var f=fn()执行后,f其实是函数in_fn()的引用,此时,函数实例fn()在f引用销毁之前不会销毁,所以,其内部变量n可以保存在内存中。

2.自调用匿名函数

自调用匿名函数,或者说,立即执行函数表达式,英文缩写iife,是一个立即执行的匿名函数表达式,我们把这种代码结构称为“自调用匿名函数”。

(function() {

    alert("我是一个自调用匿名函数");

})();

该代码定义了一个匿名函数,而且会立即执行。其实,这段代码等同于下面的代码,但这种表达方式更加简单直接。

function fn(){

};

下面的方式也是合法的。

(function(){

}());

当然,自调用匿名函数是可以带参数的。

(function(window){

    window.msg = "我被自调用匿名函数初始化";

})(window);

alert(window.msg);

第四节:命名空间

这里,自调用匿名函数执行的时候,将window对象传给了它,它给window对象设置了一个属性msg,并初始化。

很明显的是,对于自调用匿名函数,我们不能调用两次,因此,它非常适合做一次性或者初始化性质的工作。

要特别注意的是,自调用匿名函数用在赋值函数表达式中的情况。先看下面的例子。

var fn = function(){

    return "这是函数执行结果";

alert(fn);

其fn是一个赋值函数,我们打印出来的是函数的代码:

第四节:命名空间

}();

这时候fn就不是一个函数的地址了,而是匿名函数执行后的结果:

第四节:命名空间

3.命名空间

因此,我们可以通过函数作用域来实现javascrpt的命名空间 ,也就是利用自调用匿名函数,创建一个特殊的函数作用域,将该作用域中的代码和外界隔离开,从而实现命名空间的概念。

比如jquery框架的命名空间:

(function(window,undefined){

     var jquery = (function(){

          var jquery = function(){

               alert("创建jquery对象");

          };

          return jquery;

     })();

     window.jquery = window.$ = jquery;

执行$(),或者jquery(),显示:

第四节:命名空间

该代码通过自调用匿名函数创建了一个和外界隔离的对象jquery,并通过全局变量$(window.$)和jquery(window.jquery)对外公开,其jquery对象起到了命名空间的作用。同时利用闭包的特性,让命名空间对象的值始终保持在内存中。

4.模块化

虽然我们通过javascript的闭包、自调用匿名函数等特性,创建了命名空间,或者说我们创建了命名空间模式,可以很好地解决名字问题。但随着系统的增大,需要团队协作完成一个项目,此时,多文件就是必须的了。多文件开发环境下,模块化编程就是我们必须要掌握的一项技术。

我们可以通过扩展模式,来实现多人、多文件开发。比如,a定义了一个命名空间module_a,为了表述的统一,这里我们称为模块module_a。

    var module = {};

    module.moduleproperty = "模块module_a的原始属性moduleproperty";

    module.modulemethod = function () {

        alert("模块module_a的原始方法modulemethod");

    };

    window.module_a = module;

该代码写在文件file_a.js中。现在,b要负责module_a模块其他方法的实现,他可以在文件file_b.js中,通过扩展模式,给module_a添加方法、或者属性。

(function(module){

    module.anothermethod = function () {

            alert("模块module_a的扩展方法anothermethod");

        };

})(window.module_a);

下面,我们用一个测试文件测试一下:

<!doctype html>

<html lang="en">

 <head>

  <meta charset="utf-8">

  <script src="file_a.js"></script>

  <script src="file_b.js"></script>

  <title>document</title>

 </head>

 <body>

<script type="text/javascript">

module_a.modulemethod();

module_a.anothermethod();

</script>

 </body>

</html>

很好,运行顺利:

第四节:命名空间
第四节:命名空间

但是,如果我们对file_a.js和file_b.js两个文件的引用顺序错了的话,就会产生错误。

第四节:命名空间
第四节:命名空间

错误的原因是我们在扩展module_a时,module_a还没有定义,也就是还不存在。这个问题可以通过宽模式扩展解决。

file_a.js文件代码的修改:

})(window.module_a || {});

file_b.js文件代码的修改:

        alert("模块module_a的扩展方法anothermethod");

也就是说,我们在给自调用匿名函数传递参数的时候,就判断全局变量是否存在,如果不存在则直接创建一个object对象给它。这样,我们就可以可以创建灵活多变的模块,可以将他们无顺序加载,以宽松的方式扩展。