模仿块级作用域
JS中没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是包含函数中而非语句中创建的,例如:
function outputNumbers(count) {
for(var i = 0; i < count; i++) {
alert(i);
}
alert(i);
}
JS从来不会告诉你是否多次声明了同一个变量;遇到这种情况,他只会对后续的声明视而不见(不过,他会执行后续声明中的变量初始化)。匿名函数可以用来模仿块级作用域并避免这个问题。用作块级作用域(私有作用域)的匿名函数的语法如下:
(function() {
// 这里是块级作用域
})()
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。这种做法可以减少闭包占用的内存问题。
私有变量
严格来讲,JS中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包含函数的参数、局部变量和在函数内部定义的其他函数。例如:
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}
我们把有权访问私有变量和私有函数的公有方法称为特权方法。有两种在对象上创建特权方法的方式。第一种是在构造函数中定义特权方法,基本模式如下:
function MyObject() {
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
// 特权方法
this.publicMethod = function() {
privateVariable++;
return privateFunction();
}
}
这个模式在构造函数内部定义了所有私有变量和函数。然后,又继续创建了能够访问这些私有成员的特权方法。能够在构造函数中定义特权方法,是因为特权方法作为闭包有权访问在构造函数中定义的所有变量和函数。
静态私有变量
通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法,其基本模式如下:
(function() {
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
// 构造函数
MyObject = function() {
}
// 公有/特权方法
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
}
})
这个模式创建了一个私有作用域,并在其中封装了一个构造函数及相应的方法。在私有作用域中,首先定义了私有变量和私有函数,然后又定义了构造函数及其公有方法。公有方法是在原型上定义的,这一点体现了典型的原型模式。
这个模式与在构造函数中定义特权方法的主要区别,就在于私有变量和函数由实例共享的。由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。而这个特权方法,作为一个闭包,总是保存在对包含作用域的引用。
模块模式
模块模式是为单例创建私有变量和特权方法。所谓单例(singleton),指的就是只有一个实例的对象。按照惯例,JS是以对象字面量的方法来创建单例对象的。
var singleton = {
name: value,
method: function() {
// 这里是方法的代码
}
}
模块模式通过为单例添加私有变量和特权方法能够使其得到增强,其语法形式如下:
var singleton = function() {
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
// 特权/公有方法和属性
return {
publicProperty: true,
publicMethod: function() {
privateVariable++;
return privateFunction();
}
}
}
z这个模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,首先定义了私有变量和函数。然后,将一个对象字面量作为函数的值返回。返回的对象字面量中只包含可以公开的属性和方法。由于这个对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数。从本质上来讲,这个对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时有需要维护其私有变量时是非常有用的。
如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。以这种模式创建的每个单例都是Object的实例,因为最终要通过一个对象字面量来表示它。
增强的模块模式
增强的模块模式,即在返回对象之前加入对其增强的代码。这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况。