闭包的官方的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
通俗点的说法是:
-
从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
-
从实践角度:以下函数才算是闭包:
即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
在代码中引用了自由变量(自由变量:是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量)
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回收,原因如下:
-
closure返回函数inner的引用给quote
-
函数inner的作用域链包含了对函数closure的活动对象(activation_1)的引用,如下图所示。
-
inner可以访问到closure中定义的所有变量和函数
-
函数inner被quote引用
- 函数inner又依赖函数closure
二、大致的作用域图
三、closure里面的大致执行步骤
1) 初始化Global Object即window对象,Variable Object(全局执行环境中的可变对象)为window对象本身。创建Scope Chain对象,假设为scope_1,其中只包含window对象
2) 扫描JavaScript源代码,从结果中可以得到定义的变量名、函数对象。按照扫描顺序:
-
发现函数closure的定义,使用这个定义创建函数对象,传给创建过程的Scope Chain为scope_1。将结果添加到window的属性中,名字为closure,值为返回的函数对象
- 发现变量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的处理过程:
-
发现变量innerVar,在activation_1对象上添加innerVar属性,值为undefined
- 发现函数inner的定义,使用这个定义创建函数对象,传给创建过程的Scope Chain为scope_2(函数closure的Scope Chain)。将结果添加到activation_1的属性中,名字为inner,值为返回的函数对象。inner的内部 [[Scope]]就是scope_2
3.4 执行innerVar赋值语句,赋值为"0"
3.5 执行inner:
-
创建Activation Object,假设为activation_2;创建一个新的Scope Chain,假设为scope_3,scope_3中第一个对象为activation_2,接下来的对象依次为activation_1、window 对象(取自fn2的[[Scope]],即scope_2)
-
处理参数列表。因为inner没有参数,所以只用创建arguments对象并设置为activation_2的属性
-
对inner的函数体执行类似步骤2的处理过程,没有发现变量定义和函数声明
-
执行函数体。对任何一个变量引用,从scope_3上进行搜索,这个示例中,innerVar将在activation_1上找到
-
返回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_执行模型详解