[英]Array.forEach() and closure
閱讀有關JavaScript優化的文章后,我意識到有必要刪除代碼中的閉包以優化內存使用。
我的一種代碼模式是即使在以下情況下也盡可能使用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使用更多的內存。
我意識到有必要刪除代碼中的閉包以優化內存使用。
對於內存使用,只有您存儲在某處的閉包才算在內。 當遇到內存問題時,應檢查是否有很多實例的類,每個實例都有自己的關閉實例。 這並不意味着您通常應該避免關閉。
我的一種代碼模式是盡可能多地使用
Array.forEach()
別。 假設您正在使用ES6,則應盡可能使用for … of
(用於命令性循環)。
顯然,在
Array.forEach()
使用的回調函數創建了閉包
是的,但是在您展示的示例中,它們是不可避免的(無法移到靜態函數中)。 鑒於它們只持續到forEach
調用,並且將在之后立即被垃圾回收,因此也沒有內存緊張。
但是,正如您所鏈接的文章所解釋的那樣,創建閉包仍然很昂貴(與根本不創建閉包相比)。
我應該回到對性能敏感的項目中
for
循環嗎?
是的,絕對可以(至少在真正對性能敏感的位置-不會有整個項目 )。 但是,原因不是關閉的代價,而是forEach
無法完全優化的一般情況下函數的調用開銷。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.