箭头函数和内存泄漏

Arrow functions and memory leak

假设我为一个巨大数组的每个元素创建了一个箭头函数

someHugeArray.forEach(record => {
  const someValues = [...getAnotherHugeArray()]
  const sum = _.sumBy(someValues, 'total')

  record.getPrice = () => sum / record.quantity
})

这只是一个例子...所以在创建 getPrice 的环境中,我们有一个巨大的数组 someValues,我们使用它,但实际上是 getPrice我们不再需要它,因为我们获得了所需的值并将其保存到 sum

用code破坏它的值有用吗

someValues = null

或 javascript 引擎足够聪明,不会在内存中保留函数词法环境的值,它不被它使用?

由于 someValues 是一个块范围变量,它在循环完成后不再存在。

Sebastein 评论后更正

根据 MDN,通过显式删除对它的引用使其无法访问是很有用的(如果对象将在范围内停留更长时间)。

Limitation: Releasing memory manually

There are times when it would be convenient to manually decide when and what memory is released. In order to release the memory of an object, it needs to be made explicitly unreachable.

As of 2019, it is not possible to explicitly or programmatically trigger garbage collection in JavaScript.

tl;dr

  • 根据 ECMAScript,完整 词法环境被绑定
  • 在实践中,引擎优化此如果可能通过仅绑定使用变量
  • 无法进行优化,例如当 eval() 在内部使用时

我找到了一个很棒的文章系列,其中对此进行了深入讨论:

文章比较老,但仍然有效,你可以自己验证(见下文)。

对于您的示例:理论上 someValues 将被绑定(而不是垃圾收集),尽管它未在 record.getPrice 闭包中使用。但实际上只有你在那里使用的变量(sum)被绑定。 sum 被绑定的事实对 someValues 的绑定没有影响,因为 sum 派生自 someValues,但不需要进一步引用它(它是不同的它被定义为 const sum = () => _.sumBy(someValues, 'total'))

验证:在浏览器控制台执行以下命令:

(() => {
    //eval(); // <- uncomment this line for second test
    const thisIsUsed = 1;
    const isThisBound = 2;
    return () => {
        debugger;
        return ('result: ' + thisIsUsed);
    }
})()();

调试器启动后,查看 "Scope" (Chrome)。您还可以将 thisIsUsedisThisBound 添加到 "Watch" 列表中。

这是使用 Chrome(Canary,版本 85.0.4154.0)的屏幕截图:

在当前的 Firefox(版本 76.0.1)中可以观察到相同的行为。

根据 Dmitry Soshnikov 的文章,eval() 可以破坏优化。这很容易理解,因为引擎会假设可以访问任何变量。这种行为也可以验证,只需取消注释上面代码示例中的行即可。