javascript - 箭头函数和内存泄漏
问题描述
假设我为一个巨大数组的每个元素创建了一个箭头函数
someHugeArray.forEach(record => {
const someValues = [...getAnotherHugeArray()]
const sum = _.sumBy(someValues, 'total')
record.getPrice = () => sum / record.quantity
})
这只是一个例子......所以在getPrice
创建的环境中,我们有一个巨大的数组someValues
,我们使用它,但实际上getPrice
我们不再需要它,因为我们获得了所需的值并将其保存到sum
.
用代码破坏它的价值有帮助吗
someValues = null
或者 javascript 引擎足够聪明,不会为函数的词法环境保留内存值,而函数的词法环境不被它使用?
解决方案
tl;博士
- 根据 ECMAScript,绑定了完整的词法环境
- 在实践中,如果可能,引擎会通过仅绑定使用的变量来优化这一点
- 优化是不可能的,例如在
eval()
内部使用时
我发现了一个很棒的文章系列,其中对此进行了深入讨论:
- http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-1-lexical-environments-common-theory/,特别是《组合环境框架模型》及后续文章
- http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-2-lexical-environments-ecmascript-implementation/,例如“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)。您还可以将thisIsUsed
和添加isThisBound
到“观察”列表。
这是使用 Chrome(Canary,版本 85.0.4154.0)的屏幕截图:
使用当前的 Firefox(版本 76.0.1)可以观察到相同的行为。
根据 Dmitry Soshnikov 的文章,eval()
可以打破优化。这很容易理解,因为引擎然后假设可以访问任何变量。也可以验证此行为,只需取消注释上面代码示例中的行。
推荐阅读
- linq - 从开始工作日期开始几个月的工作经验 MVC
- javascript - 按属性在对象数组中查找重复项的最短方法
- gulp - Browserify 导出具有多个条目
- haskell - 如何将动态多态函数应用于动态值?
- amazon-web-services - Accessing Cognito user attributes from Appsync
- python - 无法在 Sublime Text 3 中使用 Ananconda 解释器
- codeigniter - Simple Codeigniter + HMVC working but how can i working with multiple CI instance like admin and front
- office-js - Issue with PATCH method of outlook mail REST API returning ErrorAccessDenied
- javascript - 如何使用 ng-option/ng-repeat 在选择选项上应用标题/工具提示/弹出框
- combobox - 如何在 sapui5 动态表上获取项目?