从面向对象看JavaScript(三)—— 属性权限管理

对象的私有成员

  原文再续,书接上一回。第一讲里我们提到怎么定义类,怎么创建对象。但在这些对象上创建的属性与方法,都是能被直接访问的,JavaScript并没有提供private,public,protected等属性的权限管理机制。但是,利用闭包我们完全可以实现对象属性和方法的私有化管理。

模仿块级作用域

  在讲私有化管理之前,我们必须再讲一下作用域有关的知识。JavaScript只有全局作用域与函数作用域,而没有块级作用域。但我们可以用匿名函数来模仿块级作用域。

  在任意作用域中,我们可以用以下代码实现一个短暂的函数作用域:

1
2
3
(function(){
})();

  只要闭包不作为返回值返回,它的变量对象,执行环境与作用域链就会在执行后销毁。而匿名函数也没有被任何标识符引用,因此在退出执行环境后函数对象也会被垃圾回收。上面的代码在定义了匿名函数的同时马上执行,就能够模拟块级作用域的效果。

  这种技术经常用在全局作用域中,避免向全局作用域添加过多的变量和函数。

私有变量

  由第二讲知道,某个执行环境中不能访问其作用域链以外的变量和函数。根据这个特点,我们可以把变量放在一个只能由公共接口函数访问的作用域中,这些变量便自然只能被特定的公共接口访问,而不能被直接访问,从而实现了私有化。看一个例子:

1
2
3
4
5
6
7
8
9
10
function MyObject(){
var privateVariable = 10;
function privateFunction(){
return false;
}
this.publicMethod = function(){
privateVariable++;
return privateFunction();
};
}

  publicMethod函数作为闭包,可以访问privateVarialbe以及privateFunction,但其他函数和变量由于无法看到MyObject这个作用域,所以只能通过publicMethod间接访问,由此实现了私有变量,私有函数以及公共接口。但由于publicMethod是定义在实例上而不是在原型上,因此每创建一个实例都会创建一个publicMethod方法。

静态私有变量

  通过将公共接口定义在原型上,可以实现静态私有变量,让所有实例都共享同一个私有变量。

1
2
3
4
5
6
7
8
9
10
11
12
(function(){
var name = "";
Person = function(value){
name = value;
};
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function(value){
name = value;
};
})();

  也是利用了闭包的作用域链的特点,实现静态私有变量,只不过是把公共接口定义在原型对象上。但这样的话私有变量必须被所有实例对象共享,还是要根据需求来选择定义私有变量或者静态私有变量。

模块模式

  以上说的都是自定义类型的私有变量和公共方法。但有时候我们不想定义新的类型,在整个系统中只需要对象的一个实例。那么我们可以用模块模式来定义单例对象的私有变量和公共方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
var singleton = function(){
var privateVariable = 10;
function privateFunction(){
return false;
}
var object = new CustomType();
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;
return privateFunction();
};
return object;
}();

  也是通过一个模仿块级作用域里定义的私有变量,私有方法以及闭包来实现的。

总结

  闭包是实现对象私有变量和私有函数的最重要的技术。关键在于把要保护的变量和函数放在一个特定的作用域内,而这个作用域只存在于公共接口函数的作用域链中。