JavaScript中闭包与this对象
闭包
闭包与匿名函数容易混淆。闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数
function createComparisonFunction(propertyName){
return function ( object1,object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1 < value2){
return -1;
}else if (value1 > value2){
return 1;
}else{
return 0;
}
};
}
在上面的例子中,3、4行定义value1 value2
的过程中,调用了外部函数中的变量propertyName
。即使这个内部函数返回了,而且在其他地方调用了,但仍可以访问变量propertyName
。之所以能够访问这个变量,是因为内部函数的作用域链中包含createComparisonFunction()
的作用域。要搞清楚其中的内容,就要从理解函数被调用的时候发生了什么入手。
当某个函数被调用时,会创建一个执行环境,以及相应的作用域链。然后,使用arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三级。
变量
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。下面的例子可以清晰的说明这个问题。
function createFunctions(){
var result = new Array();
for(var i=0; i<10;i++){
result[i] = function(){
return i;
};
}
}
这个函数会返回一个函数数组,表面上看似乎会返回[0,1,...]。但实际上却是返回了[10,10,...],因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以它们引用的都是同一变量i,所以它们引用的都是同一个变量i,所以最后返回都是10.但是,我们可以通过创建另外一个匿名函数强制让闭包的行为符合预期
function createFunctions(){
var result = new Array();
for(var i=0; i<10;i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
在上面的例子中,我们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给了数组。这里的匿名函数有一个参数num,也就是最终要返回的值。在调用每个匿名函数时,我们传入变量i。由于函数传参时传值的,因此i的值就会复制给num。而在这个匿名函数内部,又创建了一个访问num的闭包。这样一来,数组中每个值就可以获取num的一个副本,因此就会有不同的返回值了。
this对象
在闭包中使用this对象也可能导致一些问题。this对象是在运行时绑定的,匿名函数的执行环境具有全局性,因此其this往往指向window。但有时候由于闭包编写方式不同,这一点可能也不明显。
var name ="The Windows";
var object = {
name: "My object",
getNameFunc: function(){
return function(){
return this.name;
}
}
}
object.getNameFunc()();//The Windows
object.getNameFunc()返回了一个匿名函数,然后调用执行这个匿名函数。因为这个匿名函数不是作为某个对象的方法来调用执行,所以它的this就是wlndow对象.
var name ="The Windows";
var object = {
name: "My object",
getNameFunc: function(){
var that = this;
return function(){
return that.name;
}
}
}
object.getNameFunc()();//My Object
在定义匿名函数之前,我们把this对象赋值给一个that。定义闭包之后,闭包也可以访问这个变量。因此,就可以获得object的变量name。