javascript中的闭包②
模仿块级作用域:JavaScript没有块级作用域的概念,这个可以用下面的示例证明:
function box() {
for (var i=0; i<3; i++) {}
alert(i); //3,i不会因为离开了for块就失效
}
JavaScript不会提醒你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果初始化了,当然还会执行的)。使用模仿块级作用域(私有作用域)可避免这个问题:
function box() {
(function () {
for (var i = 0; i<3; i++) {}//这里for语句是处于块级作用域
})();
alert(i); //报错,无法访问
}
使用了块级作用域(私有作用域)后,匿名函数中定义的任何变量,都会在执行结束时被销毁。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。一般来说,我们都应该尽可能少向全局作用域中添加变量和函数。在大型项目中,多人开发的时候,过多的全局变量和函数很容易导致命名冲突,引起灾难性的后果。如果采用块级作用域(私有作用域),每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域。
在全局作用域中使用块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
私有变量
JavaScript没有私有属性的概念;所有的对象属性都是公有的。不过,却有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。
function box(){var age = 100;}//age为私有变量,外部无法访问
而通过函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。而利用这一点,可以创建用于访问私有变量的公有方法。
function Box() {
var age = 100; //私有变量
function run() {return '运行中...';}; //私有函数
this.get = function () {return age + run();};//对外公共的特权方法,对外接口
}
var box = new Box();
alert(box.get());
可以通过构造方法传参来访问私有变量。
function Person(value) {
var user = value; //这句其实可以省略
this.getUser = function () {return user;};
this.setUser = function (value){user = value;};
}
但是对象的方法,在多次调用的时候,会多次创建。可以使用静态私有变量来避免这个问题。
静态私有变量:通过块级作用域(私有作用域)中定义私有变量或函数,同样可以创建对外公共的特权方法。
(function () {
var age = 100;
function run() {return '运行中...';}
Box = function () {}; //构造方法,不用var关键词定义则该变量即使在私有作用域中也转变为全局变量
Box.prototype.go = function () {return age + run();};//原型方法
})();
var box = new Box();
alert(box.go());
上面的对象声明,采用的是Box = function () {} 而不是function Box() {} 因为如果用后面这种,就变成私有函数了,无法在全局访问到了,所以使用了前面这种。
(function () {
var user = '';
Person = function (value) { user = value;};
Person.prototype.getUser = function () {return user;};
Person.prototype.setUser = function (value) {user = value;}
})();
使用了prototype导致方法共享了,而user也就变成静态属性了。(所谓静态属性,即共享于不同对象中的属性)。
模块模式:之前采用的都是构造函数的方式来创建私有变量和特权方法。那么对象字面量方式就采用模块模式来创建。
var box = { //字面量对象,也是单例对象
age : 100, //这是公有属性,将要改成私有
run : function () {return '运行中...';};//这时公有函数,将要改成私有
};
私有化变量和函数:
var box = function () {
var age = 100;
function run() {return '运行中...';}
return {go : function () {return age + run();}};//直接返回对象
}();
字面量的对象声明,其实在设计模式中可以看作是一种单例模式,所谓单例模式,就是永远保持对象的一个实例。
增强的模块模式,这种模式适合返回自定义对象,也就是构造函数。
function Desk() {};
var box = function () {
var age = 100;
function run() {return '运行中...';}
var desk = new Desk(); //可以实例化特定的对象
desk.go = function () {return age + run();};
return desk;
}();
alert(box.go());