对于闭包的深刻理解

来源:互联网 发布:hao123淘宝 编辑:程序博客网 时间:2024/05/01 05:33

闭包(closure),在深刻了解闭包之前,要了解以下知识点:

1、全局变量

2、局部变量

3、作用域:指一个变量的作用范围,本质是一个保存变量的对象(可以叫做作用域对象)

4、作用域链:相当于一个规则,指定了引擎寻找变量的一个顺序

5、执行期上下文栈 ECS(执行环境栈:Execution Context Stack)

6、执行期上下文(EC):也就是执行环境

7、活动对象 AO(Active Object)


闭包产生的条件:

    1、受保护的变量和操作变量的函数封装在一个外层函数中定义;

    2、外层函数,要将内层函数对象返回;

    3、使用者调用外层函数,获得内层函数对象。


闭包的形成过程:

        1、在程序执行之前:

                    产生一个ECS(为了顺序放置所执行函数的EC);首先会向栈中压入主程序main,产生一个全局作用域对象window(该对象中保存着全局变量);其中引用的函数对

             象有一个scope属性引用着该全局作用域对象(也就是引用函数来自的作用域对象);

        2、在外层函数对象调用时:

                    产生一个局部作用域对象(也就是一个AO,保存着该函数的局部变量);该AO有一个parent属性指向栈前的一个作用域对象;该AO被内层函数对象的scope属性引

               用着;而由于是返回了内层函数对象,所以内层函数是被全局作用域对象所引用着的;

                    引擎会沿着AO 向parent方向找变量(每个作用域对象连接起来的结构就是作用域链),直到找到为止。

        3、当外层函数对象调用后:

                     本来没有被引用的AO 会在函数调用后而释放,然后垃圾回收。但是现在内层函数对象引用着它,且内层函数被全局作用域对象所引用着,而全局作用域对象是不会

              释放的,这就直接导致了内层函数对象不会释放,从而使外层函数的AO被留下来,所以在继续执行(被全局作用域对象引用的)内层函数对象时,作用域链上会多了一

              个外层函数对象的作用域。引擎也会经过该作用域查找变量。该作用域上的变量也就被保护起来,一直存在,可以被重用,且不会被污染。

         一个经典的setTimeout题目:

for(var i=0;i<5;i++){

setTimeout(function(){

      console.log(i);

},1000);

}

将上述代码修改一下:使得运行结果为0,1,2,3,4

解析: 页面中所有的setTimeout都会放在同一个队列中一次执行的(顺序由设定的延迟时间大小所决定的);这个队列需要等到所有可执行代码执行完毕后,才会开始

执行这个队列(不管延迟时间是否为0);所以上述题目中有5个setTimeout被放在队列中,等到可执行代码结束时,i已经为5,所以输出结果会是5个5;这时我们需要用到

闭包,在每次循环时将i保护起来,所以可以如下形成闭包(在外部,或在第一个函数参数处):

1、 for(var i=0;i<5;i++){

(function(i){

setTimeout(function(){

      console.log(i);

},1000);

})(i)

}

2、 for(var i=0;i<5;i++){

setTimeout(

(function(i){

return function(){

      console.log(i);

}

})(i),1000);

}

可以通过Chrome中的断点查看当运行到内层函数时,形成了闭包将i保护了起来,作用域链上有三个作用域对象。

0 0