Avoid Globals
JS执行环节有很多独特之处相对于其他语言来说,如全局变量和函数的使用。
JS本身的初始执行环境就是有多种多样的全局变量所定义的,这些全局变量在环境创始之初就存在了。
全局对象是一个神秘的对象,表示脚本的最外层上下文。
浏览器中 window对象往往重载并等于全局对象,因此任何在全局对象中声明的变量或者函数都为window对象的属性。
而不需要显式的将这些挂在到 window 对象上。
var color = "red";
function getColor(){
alert(color);
}
color === window.color;
getColor === window.getColor;
6.1 全局变量带来的问题
随着代码的增长,全局变量毫无疑问的会导致一些非常重要的可维护性问题。
6.1.1 命名冲突
当全局变量和全局函数越来越多的时候,发生命名冲突概率就会越来越大。各种变量,函数将会被重置,那么很多各种各样的BUG就会随之而来。
比如:
function getColor(){
alert(color);
}
中的 color 由于依赖与全局的变量,那么这种依赖关系将很难被追踪到,我在不小心重置了 color 后不知道有多少像该函数一样的函数进行了全局依赖的行为。
接下来,全局变量与一些浏览器内置API冲突的风险。
6.1.2 代码的脆弱性
一个依赖于全局变量的函数即是深耦合于上下文环境之中。环境的变化就可能会导致函数的失效。
但是当 color 被作为参数传递进函数的话,那么情况就大大不一样了。
function getColor( color ){
alert(color);
}
不在依赖全局变量,并且从与上下文的深耦合之中脱离出来。所以当定义函数尽可能的将数据置于局部变量之中,任何外部数据都应当以参数的形式传递进入函数,保证函数与其外部环境隔离开,不至于形成深度耦合的关系。
6.1.3 难以测试
任何依赖于全局变量才能正常工作的函数,只有为其重新创建完整的全局变量才能正确的测试,然后,就木有然后了。
保证函数不多全局变量有依赖,将大大增强代码的可测试性,当然不包括JS中原生的对象,如Date,Array等。
6.2 意外的全局变量
JS 中有很多陷阱会使我们一不小心就创建了全局变量,如:
function doSomething(){
var count = 10;
title = "abcdefg";
var a = b = 0;
}
对于意外的全局变量一些工具,比如JSLint和JSHint就可以起到作用了,因为意外创建全局变量并不会引起JS引擎的报错,有时候很难察觉到,而这些工具就是我们很好的预防,消除一些意外创建的情况。还有严格模式下也会报错来提醒程序猿。
6.3 单全局变量方式
YUI 引入 唯一 YUI全局变量。
jQuery 引入 $ 和 jQuery 全局变量。
Dojo 引入 dojo 全局变量。
Closure 引入 goog 全局变量。
单全局变量意味着创建一个唯一的全局对象名,将所有你的功能代码挂在到该对象上,都成为该对象的属性,从而不创建其他的全局变量。
function Book( title ){
this.title = title;
this.page = 1;
}
Book.prototype.turnPage = function( desc_num ){
this.page += desc_num;
}
var chapter1 = new Book("one");
var chapter2 = new Book("two");
var chapter3 = new Book("three");
会有好多全局变量,那么:
var MainJS = {};
MainJS.Book = function( title ){
this.title = title;
this.page = 1;
}
MainJS.Book.prototype.turnPage = function( desc_num ){
this.page += desc_num;
}
MainJS. chapter1 = new MainJS.Book("one");
MainJS. chapter2 = new MainJS.Book("two");
MainJS. chapter3 = new MainJS.Book("three");
其实,很简单,但是带来的效果确是非常不错的。
6.3.1 命名空间
JS中的命名空间,其实质就是不断的往一个定义的全局对象中,合理有规则的塞东西。
var MyGlobal = {
namespace : function( ns ){
var parts = ns.split("."),
object = this,
i, len;
if( parts[0] === "MyGlobal" ){
parts = parts.slice(1);
}
for( i = 0,len = parts.length; i<len; i++ ){
if( !object[parts[i]] ){
object[parts[i]] = {};
}
object = object[parts[i]];
}
return object;
}
}
然后可以自由的创建命名空间了,
MyGlobal.namespace("aa.bb.cc.dd");
MyGlobal.namespace("MyGlobal.aa.bb.cc.dd");
6.3.2 模块
另外一种基于单全局变量的扩充方法是使用模块。
模块是一种通用的功能片段,并不创建全局变量和命名空间,而是将这些代码存放在一个表示执行一个任务或者发布一个借口的单函数中。两种流行的模块是 YUI模块 和 异步模块定义(AMD)。
YUI模块
是使用YUI JS类库创建新模块的一种模式,写法:
YUI.add("module-name", function(Y){
// 模块正文
}, "version", { requires: ["depend1", "depend2"] });
异步模块定义 AMD
define( "module-name", ["depend1", "depend2"] , function(d1, d2){
// 模块正文
});
6.4 零全局变量
JS代码注入到页面的时候可以实现不创建全局变量。当然使用的场景不会非常多。在一段完全独立的代码,或者代码非常小且不提供任何接口的时候。
(function(win){
// 代码 不暴露任何接口
}( window ));
只要代码需要被其他的代码所依赖的时候,就不可以使用零全局变量方式。在对于代码块的代码合并时候有挺大作用。
JS执行环节有很多独特之处相对于其他语言来说,如全局变量和函数的使用。
JS本身的初始执行环境就是有多种多样的全局变量所定义的,这些全局变量在环境创始之初就存在了。
全局对象是一个神秘的对象,表示脚本的最外层上下文。
浏览器中 window对象往往重载并等于全局对象,因此任何在全局对象中声明的变量或者函数都为window对象的属性。
而不需要显式的将这些挂在到 window 对象上。
var color = "red";
function getColor(){
alert(color);
}
color === window.color;
getColor === window.getColor;
6.1 全局变量带来的问题
随着代码的增长,全局变量毫无疑问的会导致一些非常重要的可维护性问题。
6.1.1 命名冲突
当全局变量和全局函数越来越多的时候,发生命名冲突概率就会越来越大。各种变量,函数将会被重置,那么很多各种各样的BUG就会随之而来。
比如:
function getColor(){
alert(color);
}
中的 color 由于依赖与全局的变量,那么这种依赖关系将很难被追踪到,我在不小心重置了 color 后不知道有多少像该函数一样的函数进行了全局依赖的行为。
接下来,全局变量与一些浏览器内置API冲突的风险。
6.1.2 代码的脆弱性
一个依赖于全局变量的函数即是深耦合于上下文环境之中。环境的变化就可能会导致函数的失效。
但是当 color 被作为参数传递进函数的话,那么情况就大大不一样了。
function getColor( color ){
alert(color);
}
不在依赖全局变量,并且从与上下文的深耦合之中脱离出来。所以当定义函数尽可能的将数据置于局部变量之中,任何外部数据都应当以参数的形式传递进入函数,保证函数与其外部环境隔离开,不至于形成深度耦合的关系。
6.1.3 难以测试
任何依赖于全局变量才能正常工作的函数,只有为其重新创建完整的全局变量才能正确的测试,然后,就木有然后了。
保证函数不多全局变量有依赖,将大大增强代码的可测试性,当然不包括JS中原生的对象,如Date,Array等。
6.2 意外的全局变量
JS 中有很多陷阱会使我们一不小心就创建了全局变量,如:
function doSomething(){
var count = 10;
title = "abcdefg";
var a = b = 0;
}
对于意外的全局变量一些工具,比如JSLint和JSHint就可以起到作用了,因为意外创建全局变量并不会引起JS引擎的报错,有时候很难察觉到,而这些工具就是我们很好的预防,消除一些意外创建的情况。还有严格模式下也会报错来提醒程序猿。
6.3 单全局变量方式
YUI 引入 唯一 YUI全局变量。
jQuery 引入 $ 和 jQuery 全局变量。
Dojo 引入 dojo 全局变量。
Closure 引入 goog 全局变量。
单全局变量意味着创建一个唯一的全局对象名,将所有你的功能代码挂在到该对象上,都成为该对象的属性,从而不创建其他的全局变量。
function Book( title ){
this.title = title;
this.page = 1;
}
Book.prototype.turnPage = function( desc_num ){
this.page += desc_num;
}
var chapter1 = new Book("one");
var chapter2 = new Book("two");
var chapter3 = new Book("three");
会有好多全局变量,那么:
var MainJS = {};
MainJS.Book = function( title ){
this.title = title;
this.page = 1;
}
MainJS.Book.prototype.turnPage = function( desc_num ){
this.page += desc_num;
}
MainJS. chapter1 = new MainJS.Book("one");
MainJS. chapter2 = new MainJS.Book("two");
MainJS. chapter3 = new MainJS.Book("three");
其实,很简单,但是带来的效果确是非常不错的。
6.3.1 命名空间
JS中的命名空间,其实质就是不断的往一个定义的全局对象中,合理有规则的塞东西。
var MyGlobal = {
namespace : function( ns ){
var parts = ns.split("."),
object = this,
i, len;
if( parts[0] === "MyGlobal" ){
parts = parts.slice(1);
}
for( i = 0,len = parts.length; i<len; i++ ){
if( !object[parts[i]] ){
object[parts[i]] = {};
}
object = object[parts[i]];
}
return object;
}
}
然后可以自由的创建命名空间了,
MyGlobal.namespace("aa.bb.cc.dd");
MyGlobal.namespace("MyGlobal.aa.bb.cc.dd");
6.3.2 模块
另外一种基于单全局变量的扩充方法是使用模块。
模块是一种通用的功能片段,并不创建全局变量和命名空间,而是将这些代码存放在一个表示执行一个任务或者发布一个借口的单函数中。两种流行的模块是 YUI模块 和 异步模块定义(AMD)。
YUI模块
是使用YUI JS类库创建新模块的一种模式,写法:
YUI.add("module-name", function(Y){
// 模块正文
}, "version", { requires: ["depend1", "depend2"] });
异步模块定义 AMD
define( "module-name", ["depend1", "depend2"] , function(d1, d2){
// 模块正文
});
6.4 零全局变量
JS代码注入到页面的时候可以实现不创建全局变量。当然使用的场景不会非常多。在一段完全独立的代码,或者代码非常小且不提供任何接口的时候。
(function(win){
// 代码 不暴露任何接口
}( window ));
只要代码需要被其他的代码所依赖的时候,就不可以使用零全局变量方式。在对于代码块的代码合并时候有挺大作用。