闭包指的是有权访问另一个函数作用域中变量的函数。
###函数调用过程(第一次被调用时)
1. 创建执行环境(execution context);
2. 创建作用域链,并将作用域链赋值给**特殊的内部属性Scope**;
3. 使用this和arguments等初始化函数的活动对象(activation object),外部函数的活动对象AO位于第二位,外部函数的外部函数的AO位于第三位一直排下去
作用域链本质上是一个指向变量对象的指针列表,指向各变量对象,但不包含对象。在函数中访问变量时,从作用域链中顺序查找该变量。
一般来讲,函数执行完毕之后,局部活动对象会被销毁,仅保存全局对象。
对于闭包,作用域链的销毁不同。
>
> function a() {
> var b = "closure";
> return function() {
> alert(b);
> }
> }
函数a内部返回的匿名函数,会将函数a的活动对象添加到自身的作用域中,因此对于该匿名函数来说,作用域链是本身-a-window。当函数a执行结束后,其活动对象不会被销毁,因为匿名函数的作用域链仍在引用这个活动对象。只有当匿名函数被销毁,函数a的活动对象才会被销毁。
作用域链的配置机制,使得闭包只能取得包含函数中任何变量的最后一个值。因为内部函数保存了外部函数的活动对象,在内部函数中引用变量时,外部函数变量的变化,内部函数中均会同时变化。
>
> function f() {
> var results = new Array();
> for(var i = 0; i < 10; i ++) {
> results[i] = function() {
> return i;
> };
> }
> return results;
> }
注意results中包含了10个函数,每个函数返回一个值,此时每个函数返回的都是10;因为每个内部函数均包含了函数f的活动对象,包括其中的变量i,而i对应的值最终为10;
>
> function f() {
> var results = new Array();
> for(var i = 0; i < 10; i ++) {
> results[i] = function(num) {
> return function() {
> return num;
> }
> }(i);
> }
> return results;
> }
js中函数传参是传值,因此通过将i值传给匿名函数,使得每次返回的值都不一样。
###闭包中的this指针
匿名函数的执行环境具有全局性,因此一般情况下this通常指向window,但也有例外。
>
> var name = "window";
> var obj = {
> name : "obj",
> f: function() {
> return function() {
> return this.name;
> }
> }
> }
> alert(obj.f()());//window
匿名函数的执行环境是window,因此匿名函数在调用时,作用域链是window-obj;由于第一个活动对象中已经有this指针(访问的是全局的变量name),不会再去查找作用域链,因此外部对象obj的this指针不会被访问到;而当将外部函数的this指针保存到另一个变量中时,则可以访问到,因为全局对象中没有该变量。
>
> var name = "window";
> var obj = {
> name : "obj",
> f: function() {
> var this = that;
> return function() {
> return that.name;
> }
> }
> }
> alert(obj.f()());//obj