函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内。这种特性称为“闭包”。
函数定义时的作用域链到函数执行时依然有效。
var uniqueInteger=(function(){
var counter=0;
return function(){return counter++;};//返回另外一个函数
}());
这段代码定义了一个立即调用的函数(函数的开始带有左圆括号),因此是这个函数的返回值赋值给uniqueInter。当外部函数返回之后,其他任何代码都无法访问counter变量,只有内部的函数才能访问到它。像counter一样的私有变量,在同一个外部函数内定义的多个嵌套函数都可以访问它。
function counter(){
var n=0;
return {
count:function(){return n++},
reset:function(){n=0;}
};
}
var c=counter(),d=counter();//创建两个计数器
c.count() //=>0
d.count() //=>0
c.reset()
c.count() //=>0
d.count() //=>1
两个方法count()和reset()都可以访问私有变量n。每次调用counter()都会创建一个新的作用域链和一个新的私有变量。
function counter(n){//函数参数n是一个私有变量
return {
get count(){return n++;},
set count(m){
if(m>=n) n=m;
else throw Error("count can only be set to a larger value");
}
};
}
var c=counter(1000);
c.count //=>1000
c.count //=>1001
c.count=2000
c.count //=>2000
c.count=2000 //Error!
使用参数n来保存私有状态,属性存取器方法可以访问n。调用counter的函数就可以指定私有变量的初始值了。
使用闭包技术来共享私有状态的通用做法:
function addPrivateProperty(o,name,predicate){
var value;//属性值是保存在局部变量中,而没有存储在对象o中
o["get"+name]=function(){return value;};
o["set"+name]=function(v){
if(predicate && !predicate(v))
throw Error("set"+name+":invalide value "+v);
else
value=v;
};
}
var o={};
addPrivateProperty(o,"Name",function(x){return typeof x=="string";});
o.setName("A");
concole.log(o.getName());
o.setName(0);//错误类型
在同一个作用域链中定义两个闭包,这两个闭包共享同样的私有变量和变量,是一项非常重要的技术。但是要小心不希望共享的变量共享给了其他闭包。正确做法是:
function constfunc(v){return function(){return v;};}
var funcs=[];
for(var i=0;i<10;i++) funcs[i]=constfunc(i);
func[5]() //=>5
还需注意的是,this是JavaScript的关键字,而不是变量。
闭包在外部函数里是无法访问this的,除非外部函数将this转存为一个变量,以便嵌套的函数能够访问它。