JavaScript 专家:带有“{}”的块作用域和匿名函数都有助于垃圾收集吗?

dou*_*Ort 5 javascript v8 spidermonkey anonymous-function ecmascript-6

在《你不知道的 JS:作用域和闭包》一书中,Kyle simpson 指出块作用域变量有助于垃圾回收,具体示例如下:

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);
Run Code Online (Sandbox Code Playgroud)

现在上面的示例应该有助于垃圾收集,因为someReallyBigData一旦块结束,变量就会从内存中删除,与此示例不同,它对垃圾收集没有帮助:

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);
Run Code Online (Sandbox Code Playgroud)

现在我确信这个人对他提供的例子(第一个)是正确的;var但是,我想知道如果我们使用匿名 IIFE(立即调用函数表达式)以及普通的而不是{}花括号和变量,是否一切都会相同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);
Run Code Online (Sandbox Code Playgroud)

从表面上看,他们应该做同样的事情;因为就像代码someReallyBigData块执行后任何东西都不能再访问块作用域变量一样,匿名函数内的代码一旦执行,就不能被任何东西从任何地方访问。

那么,它们真的对 Javascript 引擎的垃圾收集机制有同样的效果吗?我几乎可以肯定情况就是这样,直到我用谷歌搜索“匿名函数垃圾收集”,几乎所有出现的材料都只说了负面的事情,比如“匿名函数导致内存泄漏”等。

如果有人能阐明这件事,我会很高兴。

请不要忘记我的问题对于我提供的示例来说有点具体,谢谢!

jmr*_*mrk 7

(这里是 V8 开发人员。)是的,有多种方法可以使对象不可访问,至少包括以下所有方法:

  • 将内容放入let块作用域中声明的变量中
  • 将东西放入 IIFE 中
  • 完成后清除变量(var或):letsomeReallyBigData = 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
    
    Run Code Online (Sandbox Code Playgroud)
  • 创建闭包本身并不能让事物保持活力。但是,如果闭包引用其外部作用域中的变量,那么(当然!)只要闭包存在,就无法收集这些变量。例子:

    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.
    
    Run Code Online (Sandbox Code Playgroud)
  • 优化编译器对这一切影响不大。它通常在每个函数的基础上运行,并且通常顶层没有优化(因为大多数逻辑往往在函数中)。在函数内,优化编译器非常了解对象的生命周期(这是优化编译器的一部分含义)。