詳解JS中你不知道的各種循環測速
前言
在測試循環速度之前,我們先來創建一個有 100 萬數據的數組:
const len = 100 * 10000; const arr = []; for (let i = 0; i < len; i++) { arr.push(Math.floor(Math.random() * len)); }
測試環境為:
1.電腦:iMac(10.13.6);
2.處理器:4.2 GHz Intel Core i7;
3.瀏覽器:Chrome(89.0.4389.82)
1. for 循環
for 循環是我們最常用的一種循環方式瞭,最大的好處就是結構清晰,能夠隨時 break 停止。
我們先用 10 次的測試來看下:
console.log('test for'); for (let k = 0; k < 10; k++) { console.time('for'); let sum = 0; for (let i = 0; i < len; i++) { sum += arr[i] % 100; } console.timeEnd('for'); }
最終得到的結果:
在第 1 次和第 2 次時耗時比較多,從第 3 次開始就一直維持在 1.25ms 左右。
2. while 循環和 do-while 循環
這兩個放在一起,也是他們的結構足夠像,而且也能夠隨時 break 停止。
console.log('\ntest while'); for (let k = 0; k < 10; k++) { console.time('while'); let sum = 0; let i = 0; while (i < len) { sum += arr[i] % 100; i++; } console.timeEnd('while'); } console.log('\ntest do-while'); for (let k = 0; k < 10; k++) { console.time('do-while'); let sum = 0; let i = 0; do { sum += arr[i] % 100; i++; } while (i < len); console.timeEnd('do-while'); }
while 循環和 do-while 循環的結果幾乎一樣,我們隻看下 while 循環在瀏覽器上運行的結果:
跟 for 循環的速度不相上下。
3. forEach、map 和 reduce 循環
接下來來到我們常用的數組三劍客瞭:forEach, map, reduce 等,這 3 個方法都是在 ES6 標準上新加的語法。
3.1 forEach 的簡要介紹
這幾種方法是無法停止循環的,無論使用break還是return,都無法停止整個循環。我們可以做一個測試,例如我想當遇到 3 的倍數時,即停止循環
[1, 2, 3, 4, 5].forEach((item) => { console.log(`before return: ${item}`); if (item % 3 === 0) { return; } console.log(`after return: ${item}`); });
運行結果如下:
從運行的結果可以看到,我們的 return 隻是沒有執行當時循環時後面的語句,但並沒有停止整個循環,後面的 4 和 5 依然正常輸出。
那循環是否真的像炫邁一樣停不下來嗎?並不,還有一種方式,可以停止循環。那就是拋出異常:
try { [1, 2, 3, 4, 5].forEach((item) => { console.log(`before return: ${item}`); if (item % 3 === 0) { throw new Error('break forEach'); } console.log(`after return: ${item}`); }); } catch (e) {}
在 forEach 中拋出異常後,就可以停止該循環,然後再使用try-catch捕獲異常,避免整個服務被掛掉。
雖然可以停止 forEach 的循環,但實現起來麻煩瞭不少。因此若沒有停止整個循環的需求,可以使用 forEach, map 等循環方式;否則還是要使用其他的循環方式。
3.2 forEach 等的測速
好的,接下來我們就要測試這 3 個循環方式的循環速度瞭。
// forEach 的測試: console.log('\ntest forEach'); for (let k = 0; k < 10; k++) { console.time('forEach'); let sum = 0; arr.forEach((item) => { sum += item % 100; }); console.timeEnd('forEach'); }
// map 的測試: console.log('\ntest map'); for (let k = 0; k < 10; k++) { console.time('map'); let sum = 0; arr.map((item) => { sum += item % 100; }); console.timeEnd('map'); }
// reduce 的測試: console.log('\ntest reduce'); for (let k = 0; k < 10; k++) { console.time('reduce'); let sum = 0; arr.reduce((_, item) => { sum += item % 100; }, 0); console.timeEnd('reduce'); }
因這 3 個循環的時間差不多,我這裡就隻截取瞭 forEach 的測試結果。
執行 10 次循環後,forEach 的執行時間差不多在 10.8ms 左右,比上面的 for 循環和 while 循環高瞭將近 10 倍的運行時間。
4. for-of
ES6 借鑒 C++、Java、C# 和 Python 語言,引入瞭 for…of 循環,作為遍歷所有數據結構的統一的方法。
4.1 for-of 的簡要介紹
一個數據結構隻要部署瞭 Symbol.iterator 屬性,就被視為具有 iterator 接口,就可以用 for…of 循環遍歷它的成員。也就是說,for…of 循環內部調用的是數據結構的 Symbol.iterator 方法。
for…of 循環可以使用的范圍包括數組、Set 和 Map 結構、某些類似數組的對象(比如 arguments 對象、DOM NodeList 對象)、後文的 Generator 對象,以及字符串。
for-of 拿到的就是 value 本身,而 for-in 則拿到的是 key,然後通過 key 再獲取到當前數據。
const fruits = ['apple', 'banana', 'orange', 'lemon']; for (const value of fruits) { console.log(value); // 'apple', 'banana', 'orange', 'lemon' }
4.2 for-of 的循環測速
測試 for-of 循環速度的代碼:
console.log('\ntest for-of'); for (let k = 0; k < 10; k++) { console.time('for-of'); let sum = 0; for (const value of arr) { sum += value % 100; } console.timeEnd('for-of'); }
測試結果:
在多次重復同一個循環時,前 2 次的 for-of 循環時間會比較長,得在 15ms 以上。但後續的執行,循環速度就下降到 1.5ms 左右瞭,與 for 循環的時間差不多。
5. for-in 循環
for-in 通常用於 object 類型的循環,但也可以用來循環數組,畢竟所有數據類型的祖先都是 object 類型。
console.log('\ntest for-in'); for (let k = 0; k < 10; k++) { console.time('for-in'); let sum = 0; for (let key in arr) { sum += arr[key] % 100; } console.timeEnd('for-in'); }
測試結果:
for-in 循環的測速數據很驚人,簡直是獨一檔的存在瞭,最好的時候也至少需要 136ms 的時間。可見 for-in 的循環效率真的很低。
數組類型的數據還是不要使用采用 for-in 循環瞭;Object 類型的可以通過Object.values()先獲取到所有的 value 數據,然後再使用 forEach 循環:
const obj = {}; for (let i = 0; i < len; i++) { obj[i] = Math.floor(Math.random() * len); } for (let k = 0; k < 10; k++) { console.time('forEach-values'); let sum = 0; Object.values(obj).forEach((item) => { sum += item % 100; }); console.timeEnd('forEach-values'); }
即使多瞭一步操作,循環時間也大概在 14ms 左右,要比 for-in 快很多。
6. 總結
我們把所有的循環數據放到一起對比一下,我們這裡將每個循環的測試次數調整為 100 次,橫軸是循環的次數,數軸是循環的時間:
1.for 循環、while 循環和 d-while 循環的時間最少;
2.for-of 循環的時間稍長;
3.forEach 循環、map 循環和 reduce 循環 3 者的數據差不多,但比 for-of 循環的時長更長一點;
4.for-in 循環所需要的時間最多;
每種循環的時長不一樣,我們在選擇循環方式時,除瞭考慮時間外,也要考慮到語義化和使用的場景。
以上就是詳解JS中你不知道的各種循環測速的詳細內容,更多關於JS中各種循環測速的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- JS中數組常用的循環遍歷你會幾種
- JavaScript循環遍歷的24個方法,你都知道嗎
- 關於reduce的介紹及用法說明
- 淺談JS數組內置遍歷方法有哪些和區別
- JS數組reduce你不得不知道的25個高級用法