码迷,mamicode.com
首页 > 其他好文 > 详细

第四节:命名空间

时间:2017-07-31 14:39:39      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:闭包   自调用匿名函数   命名空间   模块化编程   

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


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


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() {

    var n = 1;

    alert(n++);

}

fn();

fn();

fn();


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


function fn() {

    var n = 1;

    function in_fn(){

        alert(n++);

    }

    return in_fn;

}

var f = fn();

f();

f();

f();


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


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


2.自调用匿名函数


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


(function() {

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

})();


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


function fn(){

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

};

fn();


下面的方式也是合法的。


(function(){

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

}());


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


(function(window){

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

})(window);

alert(window.msg);

技术分享

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


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


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


var fn = function(){

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

}

alert(fn);


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


技术分享

var fn = function(){

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

}();

alert(fn);


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


技术分享


3.命名空间


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


比如JQuery框架的命名空间:


(function(window,undefined){

     var jQuery = (function(){

          var jQuery = function(){

               alert("创建JQuery对象");

          };

          return jQuery;

     })();

     window.jQuery = window.$ = jQuery;

})(window);


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

技术分享


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


4.模块化


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


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


(function(){

    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文件代码的修改:


(function(Module){

    Module.moduleProperty = "模块Module_A的原始属性moduleProperty";

    Module.moduleMethod = function () {

        alert("模块Module_A的原始方法moduleMethod");

    };

    window.Module_A = Module;

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


file_b.js文件代码的修改:


(function(Module){

    Module.anotherMethod = function () {

        alert("模块Module_A的扩展方法anotherMethod");

    };

    window.Module_A = Module;

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


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


本文出自 “老惠” 博客,转载请与作者联系!

第四节:命名空间

标签:闭包   自调用匿名函数   命名空间   模块化编程   

原文地址:http://19680411.blog.51cto.com/11767410/1952294

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!