緩存 Array.length
是老生常談的小優化。javascript
// 不緩存 for (var i = 0; i < arr.length; i++) { ... } // 緩存 var len = arr.length; for (var i = 0; i < len; i++) { ... } // 或者 for (var i = 0, len = arr.length; i < len; i++) { ... }
但之前寫過 Java 的筆者一直對這種破碎的寫法感到不適,也對這種寫法的實際優化效果產生疑問。html
且推崇這種寫法的朋友彷佛不少也是「前輩這麼說+本身想了一下以爲有道理」。java
因爲 for 循環搭配 Array.length
是極度經常使用的 JavasScript 代碼,因此仍是有必要搞清楚的。數組
通過一番摸索後筆者獲得的結論是:緩存 Array.lengh
對優化的影響沒有想象中的大,甚至可能會有所減慢。緩存
stackoverflow 上也有這個討論,For-loop performance: storing array length in a variable 。jsp
accepted 的答案是說緩存會起到加速的結果,給出了 jsPerf 測試。函數
可是有答案反對,也給出了 jsPerf 測試。oop
兩個答案的區別在於「循環不變量代碼移動(Loop-invariant code motion)」,accepted 答案的測試循環裏沒有訪問到數組,是不實際的,後面會講到。測試
從另外一篇文章 Shoud I have to cache my array’s length? 的測試結果也能夠看出緩存差異不大。優化
還有這篇 JavaScript's .length Property is a Stored Value
這篇文章 How the Grinch stole array.length access 從 V8 的 hydrogen 探討 Array.length
在 for 循環中的處理。
正如上面提到的「循環不變量代碼移動」,V8 引擎會聰明的把能肯定不變的代碼移到循環外。
因此像下面這種代碼也不會影響引擎對 Array.length
的優化:
function uncached(arr) { for (var i = 0; i < arr.length; i++) { arr[i] } }
而當在循環中調用函數時,V8 會嘗試將函數進行內聯,從而繼續進行「循環不變量代碼移動」優化。
但當函數不可內聯時,V8 就沒轍了,每次循環都要從新計算一遍 length
function BLACKHOLE(sum, arr) { try { } catch (e) { } } function uncached(arr) { var sum = 0; for (var i = 0; i < arr.length; i++) { sum += arr[i]; if (sum < 0) BLACKHOLE(arr, sum); } return sum; }
但這時即使是在循環外緩存了 length
也是沒有用的,引擎無法預判數組的變化,當須要訪問數組元素時會觸發 bounds check ,從而照樣要計算一遍 length
。因此緩存 length
是沒有用的。
甚至,因爲多了一個變量,底層的寄存器分配器每次循環還要多一次恢復這個變量。固然這個只有在大規模的狀況下才會看出區別。
固然這篇文章也有侷限性,僅僅討論了 V8 引擎,也沒有討論訪問 length
代價更高的 HTMLCollection
。但這已經足夠說明二者差異不大,通常狀況下咱們不用再侷限於緩存的寫法,能夠放開來按照本身喜歡的方式去寫循環了。
【完】
原文:http://div.io/topic/966