JavaScript闭包的理解
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数的内部创建另一个函数。
1.内部函数可以访问外部函数的变量
一般来讲,当函数执行完毕后局部活动对象会被销毁,内存中仅保存全局作用域,但是闭包有所不同。
在一个函数内部定义的函数会将外部函数的活动对象加在它的作用域链中。counter函数内部返回了increment,increment的作用域链会被初始化为包含外部函数(即counter)的活动对象和全局变量对象,因此就可以访问在外部函数中定义的变量count。counter函数在执行完毕后,它的活动对象也不会销毁,因为increment函数的作用域链仍在引用这个活动对象。
在本例中counterOne调用了两次,第二次调用仍然保留上次调用对结果的影响。此外,在一个闭包内对变量的修改,并不会影响到另外一个闭包中的变量。
2.闭包与循环
由于作用域链的配置机制的副作用,闭包只能取得包含函数中任何变量的最后一个值。如:
得到的result数组,并没有达到预期的结果,不同的索引项对应的值都为5。这是因为每个函数的作用域链中都保存着count()函数的活动对象,所以引用的都是同一个变量i。当count()函数返回后,变量i的值是5,此时每个函数都引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都为5。
为解决该问题可采用下面几种写法:
(1)添加闭包
在该方法中,并没有直接把闭包直接赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋给数组。在调用每个匿名函数时,传入了参数i,由于函数参数是按值传递的,所以将变量i的值赋值给参数n。而在这个匿名函数的内部,又创建并返回了一个访问n的闭包。这样避免了闭包直接取得包含函数中的变量的值,可以实现数组中每一项存储对应的索引值。
(2)匿名闭包
使用匿名闭包,立即将当前循环项的i与事件回调相关联起来,可得到预期的结果。
最后要强调的一点是,由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,过度使用闭包可能会导致内存占用过多的问题,影响性能。