詳解JS中你不知道的各種循環(huán)測速
前言
在測試循環(huán)速度之前,我們先來創(chuàng)建一個有 100 萬數(shù)據(jù)的數(shù)組:
const len = 100 * 10000;
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(Math.floor(Math.random() * len));
}
測試環(huán)境為:
1.電腦:iMac(10.13.6);
2.處理器:4.2 GHz Intel Core i7;
3.瀏覽器:Chrome(89.0.4389.82)
1. for 循環(huán)
for 循環(huán)是我們最常用的一種循環(huán)方式了,最大的好處就是結(jié)構(gòu)清晰,能夠隨時 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');
}
最終得到的結(jié)果:

在第 1 次和第 2 次時耗時比較多,從第 3 次開始就一直維持在 1.25ms 左右。
2. while 循環(huán)和 do-while 循環(huán)
這兩個放在一起,也是他們的結(jié)構(gòu)足夠像,而且也能夠隨時 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 循環(huán)和 do-while 循環(huán)的結(jié)果幾乎一樣,我們只看下 while 循環(huán)在瀏覽器上運行的結(jié)果:

跟 for 循環(huán)的速度不相上下。
3. forEach、map 和 reduce 循環(huán)
接下來來到我們常用的數(shù)組三劍客了:forEach, map, reduce 等,這 3 個方法都是在 ES6 標(biāo)準(zhǔn)上新加的語法。
3.1 forEach 的簡要介紹
這幾種方法是無法停止循環(huán)的,無論使用break還是return,都無法停止整個循環(huán)。我們可以做一個測試,例如我想當(dāng)遇到 3 的倍數(shù)時,即停止循環(huán)
[1, 2, 3, 4, 5].forEach((item) => {
console.log(`before return: ${item}`);
if (item % 3 === 0) {
return;
}
console.log(`after return: ${item}`);
});
運行結(jié)果如下:

從運行的結(jié)果可以看到,我們的 return 只是沒有執(zhí)行當(dāng)時循環(huán)時后面的語句,但并沒有停止整個循環(huán),后面的 4 和 5 依然正常輸出。
那循環(huán)是否真的像炫邁一樣停不下來嗎?并不,還有一種方式,可以停止循環(huán)。那就是拋出異常:
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 中拋出異常后,就可以停止該循環(huán),然后再使用try-catch捕獲異常,避免整個服務(wù)被掛掉。
雖然可以停止 forEach 的循環(huán),但實現(xiàn)起來麻煩了不少。因此若沒有停止整個循環(huán)的需求,可以使用 forEach, map 等循環(huán)方式;否則還是要使用其他的循環(huán)方式。
3.2 forEach 等的測速
好的,接下來我們就要測試這 3 個循環(huán)方式的循環(huán)速度了。
// 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 個循環(huán)的時間差不多,我這里就只截取了 forEach 的測試結(jié)果。

執(zhí)行 10 次循環(huán)后,forEach 的執(zhí)行時間差不多在 10.8ms 左右,比上面的 for 循環(huán)和 while 循環(huán)高了將近 10 倍的運行時間。
4. for-of
ES6 借鑒 C++、Java、C# 和 Python 語言,引入了 for...of 循環(huán),作為遍歷所有數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一的方法。
4.1 for-of 的簡要介紹
一個數(shù)據(jù)結(jié)構(gòu)只要部署了 Symbol.iterator 屬性,就被視為具有 iterator 接口,就可以用 for...of 循環(huán)遍歷它的成員。也就是說,for...of 循環(huán)內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的 Symbol.iterator 方法。
for...of 循環(huán)可以使用的范圍包括數(shù)組、Set 和 Map 結(jié)構(gòu)、某些類似數(shù)組的對象(比如 arguments 對象、DOM NodeList 對象)、后文的 Generator 對象,以及字符串。
for-of 拿到的就是 value 本身,而 for-in 則拿到的是 key,然后通過 key 再獲取到當(dāng)前數(shù)據(jù)。
const fruits = ['apple', 'banana', 'orange', 'lemon'];
for (const value of fruits) {
console.log(value); // 'apple', 'banana', 'orange', 'lemon'
}
4.2 for-of 的循環(huán)測速
測試 for-of 循環(huán)速度的代碼:
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');
}
測試結(jié)果:

在多次重復(fù)同一個循環(huán)時,前 2 次的 for-of 循環(huán)時間會比較長,得在 15ms 以上。但后續(xù)的執(zhí)行,循環(huán)速度就下降到 1.5ms 左右了,與 for 循環(huán)的時間差不多。
5. for-in 循環(huán)
for-in 通常用于 object 類型的循環(huán),但也可以用來循環(huán)數(shù)組,畢竟所有數(shù)據(jù)類型的祖先都是 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');
}
測試結(jié)果:

for-in 循環(huán)的測速數(shù)據(jù)很驚人,簡直是獨一檔的存在了,最好的時候也至少需要 136ms 的時間??梢?for-in 的循環(huán)效率真的很低。
數(shù)組類型的數(shù)據(jù)還是不要使用采用 for-in 循環(huán)了;Object 類型的可以通過Object.values()先獲取到所有的 value 數(shù)據(jù),然后再使用 forEach 循環(huán):
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');
}
即使多了一步操作,循環(huán)時間也大概在 14ms 左右,要比 for-in 快很多。
6. 總結(jié)
我們把所有的循環(huán)數(shù)據(jù)放到一起對比一下,我們這里將每個循環(huán)的測試次數(shù)調(diào)整為 100 次,橫軸是循環(huán)的次數(shù),數(shù)軸是循環(huán)的時間:

1.for 循環(huán)、while 循環(huán)和 d-while 循環(huán)的時間最少;
2.for-of 循環(huán)的時間稍長;
3.forEach 循環(huán)、map 循環(huán)和 reduce 循環(huán) 3 者的數(shù)據(jù)差不多,但比 for-of 循環(huán)的時長更長一點;
4.for-in 循環(huán)所需要的時間最多;
每種循環(huán)的時長不一樣,我們在選擇循環(huán)方式時,除了考慮時間外,也要考慮到語義化和使用的場景。
以上就是詳解JS中你不知道的各種循環(huán)測速的詳細(xì)內(nèi)容,更多關(guān)于JS中各種循環(huán)測速的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
bootstrap模態(tài)框彈出和隱藏,動態(tài)改變中間內(nèi)容的實例
今天小編就為大家分享一篇bootstrap模態(tài)框彈出和隱藏,動態(tài)改變中間內(nèi)容的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
javascript對象的property和prototype是這樣一種關(guān)系
javascript對象的property和prototype是這樣一種關(guān)系...2007-03-03

