MDN的解释:
闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。
创建闭包的通常方式,是在一个函数内部创建另一个函数。
由于作用域链的结构,外围函数是无法访问内部变量的,为了能够访问内部变量,我们就可以使用闭包,闭包的本质还是函数。
理解闭包,必须弄清执行环境,作用域这些概念,上篇文章有总结。
例:
难点一: 判断作用域指向的变量对象是否相同
|
|
每次执行A函数时,都会生成一个A的活动变量和执行环境,执行完毕以后,A的执行环境销毁,但是活动对象由于被闭包函数引用,所以仍然保留,最终剩下两个A的变量对象,因此m1和m2在操作x时,指向的是不同的数据。
理清了这个过程,下面三个问题就很容易回答了:
问题1:为什么连续执行m1的时候,x的值在递增?
回答:因为m1在引用的活动对象A一直没有释放(想释放的话可以让m1=null),所以x的值一直递增。
问题2:定义函数m2的时候,为什么x的值重新从1开始了?
回答:因为又一次运行了A函数,生成一个新的A的活动对象,所以m2的作用域链引用的是一个新的x值。
问题3: m1和m2里面的x为什么是相互独立,各自维持的?
回答:因为在定义m1和m2的时候,分别运行了A函数,生成了两个活动对象,所以,m1和m2的作用域链是指向不同的A的活动对象的。
难点二: 判断变量对象中变量的值
下面是常见的列子:
解释:
当执行 var funs = A();
时,只是定义,而没执行,产生环境变量的是在console.log(funs[0]());
此时A的变量对象中i值已经是10,自然返回的都是10。
解决办法1:用ES6
的 let
代替 var
。
解决办法2:用立即执行函数:
tmp1
是立即执行函数。定义之后立即执行,即把i的值立即传递给n,然后再返回tmp2
,此时执行10次,生成了10个tmp2
的活动变量。每个tmp2
变量对象保存的就是循环i的值。