闭包:是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另一个函数
function createComparisonFunction(propertyNmae){
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;
}
};
}
1. 闭包产生的原因
在函数createComparisonFunction()中返回了一个匿名函数,创建了一个闭包。
当匿名函数被返回时,其作用域链包含外部函数createComparisonFunction的作用域链,这样匿名函数就能访问外部函数createComparisonFunction定义的变量了。(因为作用域链的访问机制)更重要的是外部函数执行完毕后,其作用域链销毁,但其活动对象不会销毁,因为在匿名函数的作用域链在引用它,直到匿名函数被销毁才会销毁。
2. 作用域链
作用域链的最前端是当前代码的执行环境的变量对象(活动对象),下一个变量对象是其的包含环境,再下一个是下一个的包含环境,这样作用域链的末端就是全局执行环境。内部环境在作用域链的最前端,外部环境在其后面。由于标识符解析沿着作用域链一级一级的搜索标识符直到找到。因此内部环境可以通过作用域链访问到所有外部环境,但是外部环境不能访问内部环境的任何变量和函数。
3.对于上图的解析
对于外部函数createComparisonFunction来说,它的作用域链最前端是自己的活动对象,里面存储着atguments和propertyName,接下来是它的包含环境即全局变量对象;对于匿名函数,它的作用域链最前端就是闭包本身的活动对象,里面存储arguments和object1,object2.接下来是其包含环境createComparisonFunction的活动对象,最后就是全局活动对象。
4.闭包和变量
闭包保存的是整个变量的对象而不是变量的某个特殊值,因此闭包智能取得任何变量的最后一个值。
function createFunction(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(){
return i;
};
}
return result;
}
上面的函数结果输出全是10,这是因为每次调用匿名函数时其作用域链保存的都是createFunction()函数的活动对象,因此引用的都是同一个变量i。当createFuction()返回后,变量i的值都是10,此时每个匿名函数都引用者保存变量i 的同一个变量,所以在每个函数内部i的值都是10
那么要如何让闭包符合预期呢?
function createFunction(){
var result=[];
for(var i=0;i<10;i++){
result[i]=function(num){
return function(){
return num;
};
}(i);
}
return result;
}
这样修改后能返回不同索引值是因为:没有把某一时刻i的特殊主直接赋值给数组,而是定义了一个立即执行的匿名函数将其赋值给数组。在这个匿名函数里面,接受i的特殊值作为参数,由于立即执行,此时将i的特殊值按值传递给num,而在立即执行的匿名函数内部又创建了一个访问num 的闭包,这样result数组的每个函数都有自己的num副本(保存i的特殊值),就让闭包的结果符合预期。
5.关于this对t象
匿名函数的执行环境有全局性,其this对象通常指向window
var name='the window';
var object={
namw:'my object',
getNamFunc:function(){
return function(){
return this.name;
}
}
};
alert(object.getNameFunc()()); //the window
上面调用object.getNameFunc(),返回的是the window而不是没有my object
我们把最后的一句拆成两个步骤执行:
var first = object.getNameFunc();
var second = first();
其中第一步,获得的first为返回的匿名函数,此时的getNameFunc()作为object的方法调用,如果在getNameFunc()中使用this,此时的this指向的是object对象。
第二步,调用first函数,可以很清楚的发现,此时调用first函数,first函数没有在对象中调用,因此是作为函数调用的,是在全局作用域下,因此first函数中的this指向的是window。
由于每个函数在被调用时会自动取得this.arguments,函数搜索时只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。
那么,如何获得外部作用域中的this呢?
可以把外部作用域中的this保存在闭包可以访问到的变量里。如下:
var name = "The Window";
var object = {
name: "My object",
getNameFunc: function() {
var that = this; // 将getNameFunc()的this保存在that变量中
var age = 15;
return function() {
return that.name;
};
}
}
alert(object.getNameFunc()()); // "My object"
复制代码
其中,getNameFunc()执行时的活动对象有:that/age/匿名函数,在执行匿名函数时,同时引用了getNameFunc()中的活动对象,因此可以获取that和age的值。但是由于是在全局环境中调用的匿名函数,因此匿名函数内部的this还是指向window。