日期:2014-05-16  浏览次数:20366 次

js闭包的一些思考

js闭包问题曾经不止一次的困扰过我,在我反复的看了一些资料以后,有以下体会,希望与大家分享:

?

1、闭包出现的场景

???????产生闭包至少需要4个元素:外部变量、外层函数、外层函数的局部变量和内层函数,这几个元素的持有关系使得彼此都不会被垃圾回收,从而形成了一个闭环(个人认为这是闭包名字的由来),简单一点说就是外部变量持有内层函数的句柄,内层函数又持有外层函数的变量。看下面函数:

var ou = [];
function ouf(){
     for(var i=0;i<3;i++){
            var obj = {};
            obj.num=function(){
                          alert(i);
                    };
            ou.push(obj);
     }
};

ouf();
ou[0].num();
ou[1].num();
ou[2].num();

这里ou持有内部函数num的句柄,而num中又有外层函数ouf的局部变量i(for中的i 跟放在外面一样),所以只要外部变量ou存在,i 跟ouf就不会被回收。

2、闭包导致的问题

??????? 从上面的运行结果来看,ou[0].num();ou[1].num();ou[2].num(); 三个函数的结果都是3,这可能与我们之前期望的0,1,2有所不同,问题出在什么地方呢?我们可以从两个方面理解:

??????? (1)当我们执行ouf()的时候,我们并没有真正的执行num函数,我们仅仅是给obj的num属性传递了一个函数的地址指向,也就是说将来我们要访问obj的num属性的时候,num会根据这一地址去找对应的函数,而在这个函数内部,又存在一个i 的“地址”(由于i是基本类型,说地址不太合适)。只有当我们要执行num的时候,num函数才会去访问i ,而此时的i 的数值已经发生了改变,增加到了3.

??????? (2)我们可以近似的将此处的i 看成num函数的“全局变量”(或者java中的静态变量),所有对i的操作都会影响到将来每个调用num函数的情况。

3、闭包问题的解决

??????? 解决闭包问题本质上就是将i这个“全局变量”转化为num的局部变量(这相当于将java中的静态变量转化为实例变量)。对上面的代码做一些修改:

var ou = [];
function ouf(){
     for(var i=0;i<3;i++){
            var obj = {};
            obj.num=(function(s){
                          return function(){
                                         alert(s);
                                    }
                        })(i);
            ou.push(obj);
     }
};

ouf();
ou[0].num();
ou[1].num();
ou[2].num();

(function(s){?

????????return function(){
???????????????????????alert(s);
?????????????????}
? })(i);
????? 这行代码在js中是将i 作为参数传给外面的匿名函数,并立即执行的意思。为什么这样就能解决闭包的问题了呢,我们可以看到,这里的s其实就是外面函数的局部变量,return function(){?alert(s);?}仍旧是给obj的num属性指向了一个函数,惟一的区别就是此时这个函数里的变量s变成了局部变量。那么从整体来看,全局变量i 在每一次改变之后,将改变后的不同值分别传给了不同obj对象的num函数的局部变量,这样当我们以后执行num函数的时候,实际上访问的是每个obj自己的num函数对应的自身的变量。当ouf()执行完之后,i 就没有了指向,等待被垃圾回收……

?

?

?

以上“言论”为个人理解,不当之处,欢迎拍砖……