对JS闭包的理解
每当我们声明一个函数,这个函数就会有闭包域,这个闭包域也就是我们常说的闭包。
大家都知道的两点:
1.闭包域内声明的变量和方法,外部无法访问到。
2.闭包域可以访问到外部的变量和方法。
这很好理解,子函数的生命周期依附于父函数,父函数内的变量(从全局看是私有变量),对于子函数来讲是全局的。
当在一个闭包域中又包含另一个闭包域(也就是在函数内部声明另一个函数),子闭包域即当前的子函数对象的function scopes会产生一个closure对象属性,在这个对象中包含子闭包域对父闭包域的所有引用。
先来一发代码:
1 | var foo = function () { |
foo2是子函数,接下来我们看一下Chrome Debugger的内容:
先插一句:function scope不是对象,不是属性,我对他的理解是JavaScript引擎对函数的一种参数引用机制。
可以清楚地看到,foo2的function scopes内有Closure和Global两个对象属性,foo2这个函数对象所引用的i值为6。
倘若在子闭包域仍旧存活的状态下(当然此时父函数依然在栈中),父函数的私有变量其值出现变化。那么,对这个私有变量进行引用的子闭包域内的closure对象内的引用值也会发生变化,因为其实质是引用。
接下来,举一个经典的例子:
1 | function foo () { |
运行后程序会如注释所展示的那样,每一个ele的结果都是一样的,也就是i最后的值3的平方。只要知道实质是引用这一点,也就很好理解为什么会出现这样的结果了。
当调用了foo()的时候,foo内部的私有变量result数组内的每一个元素都被push进了一个需要对另一个私有变量i进行引用的function,这些匿名函数的function scope内的closure都会有对当前i的引用,函数体内的i的值取决于函数调用时的i的值。
现在我们来试着令其正常输出为0,1,4.
第一种思路:通过函数传参立即执行以保存状态。
1 | function foo() { |
第二种思路:通过Function构造器,以字符串拼接的形式保存状态。
1 | function foo() { |
在ES6中,新增了let关键字,使得变量获得了块级作用域。
1 | function foo() { |