事件监听器是否会阻止垃圾回收外部作用域未引用对象?
关于JavaScript事件监听器与垃圾回收的疑问
示例代码
(function () { const largeObject = provideSomeLargeObject(); const largeStaticListOfElements = document.querySelectorAll('span'); const someElementThatWillBeRemoved = document.getElementById('i-will-be-removed'); const elms = document.getElementsByClassName('click-me'); for (let i = 0; i < elms.length; ++i) { const anotherLargeObject = provideAnotherLargeObject(); elms[i].addEventListener('click', function (e) { // callback just uses i, which contains a literal value e.preventDefault(); console.log(i); }); } // largeObject, largeStaticListOfElements, someElementThatWillBeRemoved, and anotherLargeObject // are no longer used in this code. Should they be set to null to allow GC them? })();
问题
- 事件监听器是否会阻止这些变量引用的对象被垃圾回收,是否需在代码末尾手动将变量设为null?
- 还是JavaScript引擎足够智能,能检测到事件处理器从未使用这些变量,即使变量位于仍存在的函数作用域中,其引用的对象仍可被垃圾回收?
回答
不用手动把这些变量设为null,现代JavaScript引擎(比如Chrome的V8、Firefox的SpiderMonkey)都具备智能的作用域分析能力:
- 引擎会精准识别闭包(即点击事件的回调函数)实际依赖的外层变量,这里回调只用到了
i,其他变量(largeObject、largeStaticListOfElements、someElementThatWillBeRemoved)完全没被回调引用,所以这些变量对应的对象不会被闭包持有,垃圾回收器会在合适时机正常清理它们。 - 循环里的
anotherLargeObject属于每轮循环的块级作用域,每个回调都没引用它,所以每轮循环结束后,这个变量对应的对象就可以被回收,不会被留存。 - 只有当闭包明确引用了某个外层变量时,该变量才会被保留在作用域中;未被用到的变量,哪怕外层作用域因为其他活跃闭包(这里是用到
i的回调)而存在,也不会阻碍其引用对象的垃圾回收。
内容的提问来源于stack exchange,提问作者Gras Double
相关产品推荐
相关产品推荐

