闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
function createComparisonFunction(property) {
return funtcion (f1,f2) {
return f1(property) == f2(property);
}
}
f1()
和f2()
都能访问到createComparisonFunction)
函数的property
属性,是因为f1()
和f2()
函数的作用域中包含createComparisonFunction()
的作用域。
当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,……直至作为作用域链终点的全局执行环境。
下图为下面代码实现时的函数的作用链:
var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });
从上图可以看出:
- 全局变量对象是一直存在的,而局部变量只在函数执行过程中存在。
- 函数的作用链中,函数本身的
arguments
在第一位,外部createComparisonFunction
函数的arguments
排在第二位,而全局变量对象处在第三位。
//获得其匿名函数
var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });
在匿名函数从 createComparisonFunction()
中被返回后,它的作用域链被初始化为包含createComparisonFunction()
函数的活动对象和全局变量对象。
当 createComparisonFunction()
函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后, createComparisonFunction()
的活动对象才会被销毁,
所以在清除闭包函数时,不仅需要将函数清除,也需要清除函数内的闭包。
闭包与变量
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。
如下面例子:
function createFunctions(){
var result = new Array();
for (var i=0; i < 4; i++){
result[i] = function(){
return i;
};
}
return result;//返回的结果result[0]=result[1]=result[2]=result[3]=4;
}
上面例子的作用链图如下:
可以看出i是createFunctions
函数的活动对象,所以内部函数引用的都是同一个i,当createFunctions
返回后,i为4,所以数组中值都为4。
解决上面问题方法就是创建另一个匿名函数。
function createFunctions(){
var result = new Array();
for (var i=0; i < 4; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
定义了一个匿名函数,并将立即执行该匿名函数的结果赋给数组。这样函数当时执行的i值,会立即传给num。在这个匿名函数内部,又创建并返回了一个访问 num 的闭包。这样一来, result 数组中的每个函数都有自己num 变量的一个副本,因此就可以返回各自不同的数值了。
关于this对象
this 对象是在运行时基于函数的执行环境绑定的:在全局函数中, this 等于 window,而当函数被作为某个对象的方法调用时, this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。
function func1() {
alert(this);
}
func1();//window
直接定义函数func1
,相当于在全局环境下定义,所以this指向window。
var o = {
name: "object",
func2:function() {
alert(this);
}
};
o.func2();//object
此时,函数是通过对象o调用,所以this指向object。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)
object.getNameFunc()
返回的是匿名函数,此时不是通过getNameFunc()
函数调用的,所以this指向window。
var name = "The Window";
var object = {
name : "object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"My Object"
在getNameFunc()
中this指向的My Object
,所有先赋值给that,然后匿名函数返回时,that的值仍然保持,所以最后返回的是My Object
。