在《You don't know JS: scopes & closures》一书中,Kyle simpson 指出 block 作用域变量有助于垃圾回收,具体示例如下:
function process(data) {
// do something interesting
}
{
let someReallyBigData = {};
process(someReallyBigData);
}
var btn = document.getElementById("my_button");
btn.addEventListener("click", function click(evt) {
console.log("Clicked!");
}, false);
现在上面的例子应该有助于垃圾收集,因为变量 someReallyBigData
会在 block 结束后立即从内存中删除,不像这个例子,它对垃圾收集没有帮助-收藏:
function process(data) {
// do something interesting
}
var someReallyBigData = {};
process(someReallyBigData);
var btn = document.getElementById("my_button");
btn.addEventListener("click", function click(evt) {
console.log("Clicked!");
}, false);
现在我确信这个人关于他提供的例子(第一个)是正确的;但是,我想知道如果我们使用匿名 IIFE(立即调用的函数表达式)以及普通的 var
而不是 {}
curly,是否一切都会一样大括号和 let
变量。让我把它变成一个例子:
function process(data) {
// do something interesting
}
(function(){
var someReallyBigData = {};
process(someReallyBigData);
}());
var btn = document.getElementById("my_button");
btn.addEventListener("click", function click(evt) {
console.log("Clicked!");
}, false);
从表面上看,他们应该做同样的事情;因为就像 block 作用域的 someReallyBigData
变量在代码块执行后不能再被任何东西访问一样,匿名函数中的代码一旦执行就不能被任何东西访问,任何东西,从任何地方。
那么,它们真的对 Javascript 引擎的垃圾回收机制有同样的影响吗?我几乎可以肯定是这种情况,直到我用谷歌搜索“匿名函数垃圾收集”,几乎所有出现的 Material 都只说负面的事情,比如“匿名函数导致内存泄漏”等等。
如果有人能阐明这件事,我会很高兴。
请不要忘记我的问题是针对我提供的示例的,谢谢!
最佳答案
(这里是 V8 开发人员。)是的,有几种方法可以使对象无法访问,至少包括以下所有方法:
- 将内容放入
let
block 作用域中声明的变量 - 将内容放入 IIFE
- 使用完后清除变量(
var
或let
):someReallyBigData = null;
最终结果在所有情况下都是相同的:不再可达的对象有资格进行垃圾回收。
基于此处讨论的其他注释:
问题中引用的建议对顶级代码有意义。在一个合理大小的函数中,我不会担心它——该函数可能很快就会返回,因此没有任何区别,因此您无需为这些考虑而增加负担。
“一个对象现在可以被释放”和“一个对象将现在被释放”之间有很大的区别。让某些东西超出范围不会导致它立即被释放,也不会导致垃圾收集器运行得更频繁。这只是意味着每当垃圾收集器下一次决定去寻找垃圾时,相关对象将符合条件。
无论是否匿名,IIFE 都是 IIFE。示例:
(function I_have_a_name() { var someReallyBigData = ...; })(); // someReallyBigData can be collected now. I_have_a_name(); // ReferenceError: I_have_a_name is not defined
创建闭包本身并不能使事物保持活力。但是,如果闭包在其外部范围内引用变量,那么(当然!)只要闭包存在,就无法收集这些变量。示例:
var closure = (function() { var big_data_1 = ...; var big_data_2 = ...; return function() { return big_data_1.foo; } })(); // big_data_2 can be collected at this point. closure(); // This needs big_data_1. // big_data_1 still cannot be collected, closure might need it again. closure = null; // big_data_1 can be collected now.
优化编译器对这一切影响不大。它通常在每个函数的基础上运行,并且通常不会优化顶层(因为大多数逻辑往往在函数中)。 在一个函数中,优化编译器非常了解对象的生命周期(这是优化编译器的一部分)。
关于JavaScript 专家 : Do block-scopes with `{}` and anonymous functions both help garbage-collection?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46221001/