ES6:闭包

179 阅读3分钟

这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

概念

闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。

使用

image.png

这里加粗的代码位于内部函数(匿名函数)中,其中引用了外部函数的变量propertyName。在这个内部函数被返回并在其他地方被使用后,它仍然引用着那个变量。这是因为内部函数的作用域链包含createComparisonFunction()函数的作用域。这个匿名函数就是典型的闭包,闭包会把其包含函数的活动对象添加到自己的作用域链中。

执行结果

image.png image.png createComparisonFunction()的活动对象并不能在它执行完毕后销毁,因为匿名函数的作用域链中仍然有对它的引用。在createComparisonFunction()执行完毕后,其执行上下文的作用域链会销毁,但它的活动对象仍然会保留在内存中,直到匿名函数被销毁后才会被销毁

改进方法:createComparisonFunction函数被保存在变量compareNames中。把compareNames设置为等于null会解除对函数的引用,从而让垃圾回收程序可以将内存释放掉。作用域链也会被销毁,其他作用域(除全局作用域之外)也可以销毁。

image.png

慎用闭包

1. 因为闭包会保留它们包含函数的作用域,所以比其他函数更占用内存。过度使用闭包可能导致内存过度占用。

2. 在闭包中使用this会让代码变复杂。

(1) 匿名函数没有使用箭头函数定义,则this对象会在运行时绑定到执行函数的上下文。

(2) 在全局函数中调用,则this在非严格模式下等于window,在严格模式下等于undefined。

(3) 作为某个对象的方法调用,则this等于这个对象。但是,匿名函数在这种情况下不会绑定到某个对象,这就意味着this会指向window,除非在严格模式下this是undefined。这时闭包的问题又出现了:

image.png

这里我们本身想让它返回My Object(函数内部的属性) 结果他返回的是window对应的属性

this和arguments都是不能直接在内部函数中访问的。如果想访问包含作用域中的arguments对象,但将其引用先保存到闭包就能实现内部函数访问外部函数。

image.png 在定义匿名函数之前,先把外部函数的this保存到变量that中。然后在定义闭包时,就可以让它访问that。即使在外部函数返回之后,that仍然指向object,所以调用object.getIdentityFunc()()就会返回"MyObject"。

3. 内存泄漏

由于IE在IE9之前对JScript对象和COM对象使用了不同的垃圾回收机制,在这些版本的IE中,把HTML元素保存在某个闭包的作用域中,就相当于宣布该元素不能被销毁。

image.png 以上代码创建了一个闭包,即element元素的事件处理程序,接着又创建了一个循环引用。只要这个匿名函数存在,就会对element产生引用,内存不会被回收。

image.png

在这个修改后的版本中,闭包改为引用一个保存着element.id的变量id,从而消除了循环引用。但是,闭包还是会引用包含函数的活动对象,而其中包含element。即使闭包没有直接引用element,包含函数的活动对象上还是保存着对它的引用。因此,必须再把element设置为null。这样就解除了对这个COM对象的引用,其引用计数也会减少,从而确保其内存可以在适当的时候被回收。