闭包的官方的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

通俗点的说法是:

  1. 从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。

  2. 从实践角度:以下函数才算是闭包:

            即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
            在代码中引用了自由变量(自由变量:是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量)
        function testFreedom() {
            var freedomVar = 1;
            function inner(param) {
                echo(pclosure1, param + freedomVar);//将结果打印到pclosure1中
            }
          return inner;
        }

对于inner函数来说,freedomVar就属于自由变量。

 

一、一个实现

<p id="closure1" style="color:red"></p>
     function echo(p, html) {
            p.innerHTML += html + '<br/>';
        }
     function closure() {
            var innerVar = 0;
            function inner() {
                return ++innerVar;
            }
            return inner;
        }
        var quote = closure();
        echo(pclosure1, quote());//1
        echo(pclosure1, quote());//2

先看看结果情况:

1、第一次quote函数的结果为1,第二次为2

2、inner嵌套在函数closure内部;函数closure返回函数inner

3、结合上面的闭包实践角度特点,可以得出closure是一个闭包

4、函数closure在返回后不会被GC回收,原因如下:

  1. closure返回函数inner的引用给quote

  2. 函数inner的作用域链包含了对函数closure的活动对象(activation_1)的引用,如下图所示。

  3. inner可以访问到closure中定义的所有变量和函数

  4. 函数inner被quote引用

  5. 函数inner又依赖函数closure

 

二、大致的作用域图

 

三、closure里面的大致执行步骤

1) 初始化Global Object即window对象,Variable Object(全局执行环境中的可变对象)为window对象本身。创建Scope Chain对象,假设为scope_1,其中只包含window对象

2) 扫描JavaScript源代码,从结果中可以得到定义的变量名、函数对象。按照扫描顺序:

  1. 发现函数closure的定义,使用这个定义创建函数对象,传给创建过程的Scope Chain为scope_1。将结果添加到window的属性中,名字为closure,值为返回的函数对象

  2. 发现变量quote,在window对象上添加quote属性,值为undefined

3) 执行函数closure,得到返回值:

  3.1 创建Activation Object,假设为activation_1;创建一个新的Scope Chain,假设为scope_2,scope_2中第一个对象为activation_1,第二个对象为window对象

  3.2 处理参数列表。创建arguments对象并进行设置,将arguments设置为activation_1的属性

  3.3 对closure的函数体执行类似步骤2的处理过程:

  1. 发现变量innerVar,在activation_1对象上添加innerVar属性,值为undefined

  2. 发现函数inner的定义,使用这个定义创建函数对象,传给创建过程的Scope Chain为scope_2(函数closure的Scope Chain)。将结果添加到activation_1的属性中,名字为inner,值为返回的函数对象。inner的内部 [[Scope]]就是scope_2

  3.4 执行innerVar赋值语句,赋值为"0"

  3.5 执行inner

  1. 创建Activation Object,假设为activation_2;创建一个新的Scope Chain,假设为scope_3,scope_3中第一个对象为activation_2,接下来的对象依次为activation_1、window 对象(取自fn2的[[Scope]],即scope_2)

  2. 处理参数列表。因为inner没有参数,所以只用创建arguments对象并设置为activation_2的属性

  3. 对inner的函数体执行类似步骤2的处理过程,没有发现变量定义和函数声明

  4. 执行函数体。对任何一个变量引用,从scope_3上进行搜索,这个示例中,innerVar将在activation_1上找到

  5. 返回inner的返回值

  3.6 返回结果

4) 打印结果

 

 

 

demo下载:

http://download.csdn.net/download/loneleaf1/8019865

 

参考资料:

http://www.nowamagic.net/librarys/veda/detail/1707 JavaScript闭包其一:闭包概论

http://www.nowamagic.net/librarys/veda/detail/1708 JavaScript闭包其二:闭包的实现

http://www.nowamagic.net/librarys/veda/detail/1709 JavaScript闭包其三:闭包的用法

http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html 简单易懂的JavaScript闭解

http://page.renren.com/601017893/note/801095804 Javascript 闭包

http://kb.cnblogs.com/page/110782/ Javascript闭包——懂不懂由你,反正我是懂了

http://coolshell.cn/articles/6731.html 理解Javascript的闭包

http://kb.cnblogs.com/page/105708/ 深入理解Javascript闭包(closure)

http://www.zhihu.com/question/20032419 动态作用域和词法域的区别是什么?

http://kangax.github.io/compat-table/es5/  ECMAScript5浏览器兼容表

http://www.nowamagic.net/librarys/veda/detail/1579 我们应该如何去了解JavaScript引擎的工作原理

http://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/ JavaScript 中的函数式编程实践

http://www.cnblogs.com/fool/archive/2010/10/19/1855266.html 理解Javascript_13_执行模型详解

 posted on 2014-10-10 10:35  咖啡机(K.F.J)  阅读(1443)  评论(3编辑  收藏  举报