在我阅读an article about JavaScript optimization之后,我意识到需要删除代码中的闭包以优化内存使用。
我的一个代码模式是尽可能使用Array.forEach()
,即使在这种情况下:
使用数组中的项修改外部对象
function updateSomething(array, toChange) {
array.forEach(item => {
toChange[item] = ''; // do something to change the object
});
}
创建嵌套的Array.forEach()
array1.forEach(item1 => {
array2.forEach(item2 => {
doSomething(item1, item2);
});
});
显然Array.forEach()
中使用的回调函数会创建闭包。在这些情况下,我是否滥用Array.forEach()
?我应该在性能敏感的项目中回到for
循环吗?
跟进
我已经使用Node v7.6.0对for
循环和Array.forEach()
函数进行了一些实验。我没有先前的性能测试经验。如果我做错了,请告诉我。
Array.forEach()
访问外部变量
// get the baseline of memory usage
gc();
let baseline = process.memoryUsage();
console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
let data = { test: 0 };
function test(data) {
let array1 = new Array(1000000).fill(1);
array1.forEach((item) => {
data.test = data.test + item;
});
}
test(data);
let final = process.memoryUsage();
console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
结果
Baseline memory usage: 2747.671875 KB
Final memory usage: 11027.34375 KB
Memory used: 8279.671875 KB
for
循环
// get the baseline of memory usage
gc();
let baseline = process.memoryUsage();
console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
let data = { test: 0 };
function test(data) {
let array1 = new Array(1000000).fill(1);
for(let i = 0; i < 1000000; i++) {
data.test = data.test + array1[i];
}
}
test(data);
let final = process.memoryUsage();
console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
结果
Baseline memory usage: 2747.453125 KB
Final memory usage: 11031.546875 KB
Memory used: 8284.09375 KB
嵌套Array.forEach()
// get the baseline of memory usage
gc();
let baseline = process.memoryUsage();
console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
let array1 = new Array(1000).fill(1);
let array2 = new Array(1000).fill(2);
array1.forEach((item, index) => {
array2.forEach(item2 => {
array1[index] = array1[index] = item2;
})
});
let final = process.memoryUsage();
console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
结果
Baseline memory usage: 2748.109375 KB
Final memory usage: 3368.5859375 KB
Memory used: 620.4765625 KB
嵌套for
循环
// get the baseline of memory usage
gc();
let baseline = process.memoryUsage();
console.log(`Baseline memory usage: ${baseline.heapUsed / 1024 } KB`);
let array1 = new Array(1000).fill(1);
let array2 = new Array(1000).fill(2);
for (let i = 0; i < 1000; i++) {
for (let j = 0; j < 1000; j++) {
array1[i] = array1[i] + array2[j];
}
}
let final = process.memoryUsage();
console.log(`Final memory usage: ${final.heapUsed / 1024} KB`);
console.log(`Memory used: ${(final.heapUsed - baseline.heapUsed) / 1024} KB`);
结果
Baseline memory usage: 2745.59375 KB
Final memory usage: 3234.2890625 KB
Memory used: 488.6953125 KB
结论
测试#1和#2的内存使用量增加的差异小于0.1%。所以它表明Array.forEach()
确实具有与传统for
循环相同的内存效率,即使它正在访问外部变量并且似乎创建闭包。魔术是在内部完成的。
注意到在测试#3中,array2.forEach()
的回调函数初始化了1000次。这可以解释为什么测试#3比测试#4使用更多的内存。
答案 0 :(得分:0)
我意识到需要删除代码中的闭包以优化内存使用。
对于内存使用,只有您存储某处的闭包才算数。当你遇到内存问题时,你应该检查是否有包含大量实例的类,每个实例都有自己的闭包实例。这并不意味着你应该避免一般的闭包。
我的一个代码模式是尽可能多地使用
Array.forEach()
别。鉴于您使用的是ES6,您应该尽可能多地使用for … of
(对于命令式循环)。
显然
Array.forEach()
中使用的回调函数会创建闭包
是的,但是在你展示的例子中,它们是不可避免的(不能移动到静态函数中)。并且鉴于它们仅在forEach
调用时持续并且将在之后立即进行垃圾收集,因此也没有内存压力。
但是,正如您链接的文章所解释的那样,创建封装的成本仍然很高(与根本不创建封装相比)。
我应该在性能敏感的项目中回到
for
循环吗?
是的,肯定(至少在真正性能敏感的位置 - 没有完整的项目)。然而,原因不是闭包的成本,而是forEach
无法完全优化的函数的调用开销。