关于函数表达式,与函数声明,匿名函数,赋值顺序可以参考:
http://www.cnblogs.com/zhouyongtao/archive/2012/11/22/2783089.html
http://www.cnblogs.com/zhouyongtao/archive/2012/11/19/2776776.html
什么是闭包?
有权访问另一个函数作用域中的变量的函数。(注意js中有函数级作用域,没有块极作用域)
最常见的方式就是在一个函数内部创建另外一个函数:
1 function rap(){ 2 var rapval='some values'; 3 return function(){ 4 console.log(rapval); 5 } 6 } 7 rap()();
即使这个函数被返回,或者在其它地方调用,它仍然可以访问变量rapval。之所以可以访问这个变量,是因为内部函数包含的rap()的作用域。
为什么会这样呢?先得从作用域链说起了。
作用域链
当某个函数第一次被调用时,会创建一个执行环境及相应的作用域,并把作用域赋值给一个特殊的内部属性即[[Scope]]。然后使用this,arguments和其他命名参数来初始化函数的活动对象。但在作用域中,外部函数对象始终处于第二位,外部函数的外部函数活动对象处于第三位,········直到作为作用域终点的全局执行环境。
1 function compare(val1,val2){ 2 if(val1<val2){ 3 return -1; 4 }else if(val1>val2){ 5 return 1; 6 }else{ 7 return 0; 8 } 9 } 10 var res=compare(5,10);
创建函数compare()时,会创建一个预告包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scompe]]属性中。当调用compare()函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。此后又有一个活动对象被推到作用域链的前端。显然,作用用域链本质上是一个指向变量对象的指针列表,它只引用不包含变量对象。通常来说,当函数执行完毕时,局部活动对象就会销毁,内在中仅仅保存全局作用域。
但是闭包情况又有所不同。
在另一个函数内部定义的函数会包含外部函数的活动对象添加到它的作用域链中。当匿名函数被返回时,它的使用域被始化为包含外部函数的活动对象与全局变量对象。外部函数执行完结后,其活动对象也不会销毁,因为匿名函数作用域链接仍在引用这个活动对象。
闭包的“副作用”
闭包只能取得包含函数中的任何变量的最后一个值。
function createFunction(){ var result=new Array(); for(var i=0;i<10;i++){ result[i]=function(){ return i; }; } return result; } var val=createFunction(); for(var i=0;i<10;i++){ console.log(val[i]());//10 }
表面上看,会输出1~10,但是实际上每个函数都返回10.因为每个函数都保存着createFunction函数的活动对象,所以它们都引用的都是同一个变量i。当这个函数返回后,变量i的值都是10,此时每个函数的引用也是10。我们可以使用另外一个闭包让这个函数与预期相符合,或者把i当成函数的一个属性。
function createFunction(){ var result=new Array(); for(var i=0;i<10;i++){ result[i]=(function(num){ return function(){ return num; } })(i); } return result; } var val=createFunction(); for(var i=0;i<10;i++){ console.log(val[i]()); }
闭包中的this值
在闭包中使用this,要注意。this的值基于运行时函数的执行环境绑定:在函数中,this等于window,而函数作为某个对象的方法调用时,this指向那个对象。但是闭包却不一定遵守这个规则。
1 var name="this window"; 2 var obj={ 3 name:'my object', 4 getName:function(){ 5 return function(){ 6 return this.name; 7 }; 8 } 9 }; 10 console.log(obj.getName()());//this window