再探闭包(二) - pod4g/tool GitHub Wiki

前段时间徒弟问了我一个问题,我觉得还挺有研究价值的。

徒弟发给我的是一张截图,截图的内容一字不差地copy到这儿:


有一点需要牢记,闭包保留了一个指向作用域的指针,所以,再给DOM元素附加闭包时,很可能会产生循环引用,
进一步导致内存泄漏,比如下面的代码“

function foo(element, a, b){

  element.onclick = function(){ /* uses a and b */ }

}

这里,即使没有使用element,闭包也保留了element,a,b的引用,由于element也保留了对闭包的引用,
这就不能被GC回收,这种情况下,可以将代码重构为:

function foo(element, a, b)}{

  element.onclick = bar(a, b);
  
}

function bar(a, b){

 return function(){ /* uses a and b */ } 

}

徒弟的问题是让我帮忙解释一下,上述中的概念。

这是一篇较老的文章,所以难免有疏漏,甚至错误之处,因为存在内存泄漏的浏览器只有IE6/7/8

且即 使没有使用element,闭包也保留了element,a,b的引用这句,本身就是错误的,现代浏览器是很聪明智能的,优化程度是很高的, 大部分引擎其实是知道要回收那些变量的,如果在闭包内,没有使用a和b,V8/SpriderMonkey/IE9+的引擎会回收a和b。IE8-及Opera的引擎 就不回收。

相关链接:关于闭包及变量回收问题

在IE8-引发内存泄漏,是因为:

element的onclick属性,指向的函数,就是一个已经闭包的函数,这个闭包所闭住的变量对象里有element对象,而element的属性又指向了这个闭包函数。导致了互相引用。这就引发了内存泄漏。(在V8/SpriderMonkey/IE9+引擎下,onclick中没有使用到element变量,就会被回收,所以不会引起内存泄漏。引起内存泄漏的情况只发生在IE8-下)

注意:问题描述中,防止内存泄漏的方法是没问题的,bar中return的函数和bar函数形成了闭包,这个闭包函数又被赋予foo中的onclick属性,此时这个闭包函数并不会和foo再次形成闭包。

javascript是词法作用域,闭包基于声明的上下文,而不是运行的上下文。