标签:
尽量不要使用全局变量,防止环境污染和命名冲突。
所以,将全局变量放在一个命名空间下,是一个好的解决方案。
1. 直接赋值
这是最基本的方法,但是它很啰嗦,你得重复书写多次变量名。好处是它很安全,并且不会产生歧义。
1 var myApp = {} 2 3 myApp.id = 0; 4 5 myApp.next = function() { 6 return myApp.id++; 7 } 8 9 myApp.reset = function() { 10 myApp.id = 0; 11 } 12 13 window.console && console.log( 14 myApp.next(), 15 myApp.next(), 16 myApp.reset(), 17 myApp.next() 18 ); //0, 1, undefined, 0
你可以在命名空间函数中使用this来指代对象,但是要考虑到函数被重新分配是this将指向不明。
1 var myApp = {} 2 3 myApp.id = 0; 4 5 myApp.next = function() { 6 return this.id++; 7 } 8 9 myApp.reset = function() { 10 this.id = 0; 11 } 12 13 myApp.next(); //0 14 myApp.next(); //1 15 var getNextId = myApp.next; 16 getNextId(); //NaN whoops!
2. 使用对象字面量方法
和上面的方法相比,下面如果要改变命名空间的名字将会很方便。但是仍然要注意使用this存在一定的风险。
1 var myApp = { 2 3 id: 0, 4 5 next: function() { 6 return this.id++; 7 }, 8 9 reset: function() { 10 this.id = 0; 11 } 12 } 13 window.console && console.log( 14 myApp.next(), 15 myApp.next(), 16 myApp.reset(), 17 myApp.next() 18 ) //0, 1, undefined, 0
3. 模块模式
模块模式的好处是它将全局变量保存在了一个马上执行的函数包装器(a function wrapper)中,函数包装器将返回一个相当于模块公共接口的对象。同时,没有在函数包装器中return 的变量将是私有变量,只对引用它的公共函数可见。
1 var myApp = (function() { 2 3 var id= 0; 4 5 return { 6 next: function() { 7 return id++; 8 }, 9 10 reset: function() { 11 id = 0; 12 } 13 }; 14 })(); 15 16 window.console && console.log( 17 myApp.next(), 18 myApp.next(), 19 myApp.reset(), 20 myApp.next() 21 ) //0, 1, undefined, 0
正如上面对象字面量方法一样,这里的命名空间名字可以很容易也很方便修改。但是对象字面量方法比较死板——它全是关于属性的定义,不能支持逻辑的书写,并且所有的属性必须被初始化且属性之间的值不能很简单的通过引用传递给另一个属性(所以,举例而言,内部闭包是不可能的)。模块模式相比而言没有上述限制,并且还拥有保护私有变量的特点。
我们也可以叫做namespace injection. 命名空间被代理(proxy)表示,代理直接指向了函数包装器(the function wrapper)内部——这意味着我们不再需要绑定一个return给命名空间。这使得命名空间的定义更加灵活。动态命名空间有模块命名模式的所有好处,并且比模块模式更加直观和易于阅读。
4. 应用一个命名空间参数 Supply a Namespace Argument
这里我们简单的将命名空间作为一个变量传递给自己执行的函数。变量id是私有变脸,因为它没有定义给context变量。
1 var myApp = {}; 2 (function(context) { 3 var id = 0; 4 5 context.next = function() { 6 return id++; 7 }; 8 9 context.reset = function() { 10 id = 0; 11 } 12 })(myApp); 13 14 window.console && console.log( 15 myApp.next(), 16 myApp.next(), 17 myApp.reset(), 18 myApp.next() 19 ) //0, 1, undefined, 0
我们甚至可以将context设置成全局对象(只要改变一个单词就可以啦!)
1 var myApp = {}; 2 (function(context) { 3 var id = 0; 4 5 context.next = function() { 6 return id++; 7 }; 8 9 context.reset = function() { 10 id = 0; 11 } 12 })(this); 13 14 window.console && console.log( 15 next(), 16 next(), 17 reset(), 18 next() 19 ) //0, 1, undefined, 0
5. 使用this关键字作为命名空间代理(a Namespace Proxy)
使用this关键字作为命名空间的好处是,this关键字是在给予的执行环境中是静态的,它不会偶然改变。
1 var myApp = {}; 2 (function() { 3 var id = 0; 4 5 this.next = function() { 6 return id++; 7 }; 8 9 this.reset = function() { 10 id = 0; 11 } 12 }).apply(myApp); 13 14 window.console && console.log( 15 myApp.next(), 16 myApp.next(), 17 myApp.reset(), 18 myApp.next() 19 ); //0, 1, undefined, 0
因为apply和call函数是将this和指定的函数对象绑定,所以上述(function(){ ...... }).apply(命名空间),匿名函数里面的this一定指向命名空间。
下面解释了apply和call函数执行环境绑定的功能:
1 var subsys1 = {}, subsys2 = {}; 2 var nextIdMod = function(startId) { 3 var id = startId || 0; 4 5 this.next = function() { 6 return id++; 7 }; 8 9 this.reset = function() { 10 id = 0; 11 } 12 }; 13 14 nextIdMod.call(subsys1); 15 nextIdMod.call(subsys2,1000); 16 17 window.console && console.log( 18 subsys1.next(), 19 subsys1.next(), 20 subsys2.next(), 21 subsys1.reset(), 22 subsys2.next(), 23 subsys1.next() 24 ) //0, 1, 1000, undefined, 1001, 0
当然,想和全局环境绑定,超级简单.....
1 nextIdMod(); 2 3 window.console && console.log( 4 next(), 5 next(), 6 reset(), 7 next() 8 ) //0, 1, undefined, 0
使用apply和call的最大好处是可以随便使用任何你想绑定的环境对象。
我没有使用内嵌的命名空间,因为它们很难跟踪,并且它们会将你的代码变得臃肿。对喜欢包装链(package chains )的怀旧Java码农而言,复杂的内嵌命名空间很符合他们的胃口。
js没有官方的命名空间的概念,所以你可以随心所欲大展身手创建自己的解决方案。
原文链接:https://javascriptweblog.wordpress.com/2010/12/07/namespacing-in-javascript/
标签:
原文地址:http://www.cnblogs.com/RachelChen/p/5424238.html