簡體   English   中英

Array.forEach()和閉包

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM