深入探究JavaScript中for循環(huán)的效率問題及相關(guān)優(yōu)化
Underscore.js庫
你一天(一周)內(nèi)寫了多少個循環(huán)了?
var i;
for(i = 0; i < someArray.length; i++) {
var someThing = someArray[i];
doSomeWorkOn(someThing);
}
這當(dāng)然無害,但這種寫法非常丑而且奇怪,這也不是真正需要抱怨的。但這種寫法太平庸了。
var i,
j;
for(i = 0; i < someArray.length; i++) {
var someThing = someArray[i];
for(j = 0; j < someThing.stuff.length; j++) {
doSomeWorkOn(someThing.stuff[j]);
}
}
你在擴(kuò)展糟糕的代碼,在你拋出一大堆if前,你已經(jīng)精神錯亂了。
我在兩年里沒有寫一個循環(huán)(loop)。
“你在說什么?”
這是真的,一個冷笑話。其實不是一個都沒有(好吧,我確實寫了幾個),因為我不寫循環(huán)(loops),我的代碼更容易理解。
怎么做的呢?
_.each(someArray, function(someThing) {
doSomeWorkOn(someThing);
})
或者更好一點:
_.each(someArray, doSomeWorkOn);
這就是underscorejs所做到的。干凈,簡單,易讀,短,沒有中間變量,沒有成堆的分號,簡單非常優(yōu)雅。
這是另外一些例子。
var i,
result = [];
for(i = 0; i < someArray.length; i++) {
var someThing = someArray[i];
// 打到這,我已經(jīng)手疼了
if(someThing.isAwesome === true) {
result.push(someArray[i]);
}
}
同樣,一個使用循環(huán)浪費(fèi)時間的典型用例。即便這些網(wǎng)站是宣傳禁煙和素食主義的,看到這些代碼我也感到義憤??纯春唵蔚膶懛ā?/p>
var result = _.filter(someArray, function(someThing) {
return someThing.isAwesome === true;
})
像underscore中的filter(過濾)的名字那樣,隨手寫的3行代碼就可以給你一個新的數(shù)組(array)。
或者你想把這些數(shù)組轉(zhuǎn)換成另外一種形式?
var result = _.map(someArray, function(someThing) {
return trasformTheThing(someThing);
})
上面三個例子在日常生活中已經(jīng)夠用了,但這些功能還不足矣讓underscore放到臺面上。
var grandTotal = 0,
somePercentage = 1.07,
severalNumbers = [33, 54, 42],
i; // don't forget to hoist those indices;
for(i = 0; i < severalNumbers.length; i++) {
var aNumber = severalNumbers[i];
grandTotal += aNumber * somePercentage;
}
underscore版本
var somePercentage = 1.07,
severalNumbers = [33, 54, 42],
grandTotal;
grandTotal = _.reduce(severalNumbers, function(runningTotal, aNumber) {
return runningTotal + (aNumber * somePercentage);
}, 0)
這個剛開始看上去可能有點怪,我查了下關(guān)于reduce的文檔,知道了它的存在。因為我拒絕使用循環(huán),所以它是我的首選。上面這些東西僅僅是入門,underscorejs庫還有一大堆牛B的功能。
30天不使用循環(huán)的挑戰(zhàn)。
在一下一個30天里,不要使用任何循環(huán),如果你看到一堆討厭和粗糙的東西,用each或者map將他們替換掉。再用一點reducing。
你需要注意到,Underscore是通往函數(shù)式編程的。一種看得見,看不見的方式。一條很好的途徑。
OurJS注*目前現(xiàn)代瀏覽器已經(jīng)支持each, filter, map, reduce方法,但underscore庫可以實現(xiàn)對舊版IE的兼容,下面是使用ES5原生方法寫的例子:
[3,4,5,3,3].forEach(function(obj){
console.log(obj);
});
[1,2,3,4,5].filter(function(obj){
return obj < 3
});
[9,8,5,2,3,4,5].map(function(obj){
return obj + 2;
});
[1,2,3,4,5].reduce(function(pre, cur, idx, arr) {
console.log(idx); //4 個循環(huán): 2-5
return pre + cur;
}); //15
//sort方法同樣很有用
[9,8,5,2,3,4,5].sort(function(obj1, obj2){
return obj1 - obj2;
});
for in與for loop
有人提出for in的效率要比for loop(循環(huán))的效率低非常多?,F(xiàn)在我們測試一下在不同瀏覽器中使用for in, for loop和forEach在處理大數(shù)組時的效率究竟如何。
目前絕大部分開源軟件都會在for loop中緩存數(shù)組長度,因為普通觀點認(rèn)為某些瀏覽器Array.length每次都會重新計算數(shù)組長度,因此通常用臨時變量來事先存儲數(shù)組長度,如:
for (var idx = 0, len = testArray.length; idx < len; idx++) {
//do sth.
}
我們也會測試一下緩存與不緩存時的性能差異。
同時在每個測試循環(huán)中添加求和運(yùn)算,來表明其不是空循環(huán)。
for (var idx = 0, len = testArray.length; idx < len; idx++) {
//do sth.
}
我們也會測試一下緩存與不緩存時的性能差異。
同時在每個測試循環(huán)中添加求和運(yùn)算,來表明其不是空循環(huán)。
測試代碼如下,點擊運(yùn)行即可查看
HTML 代碼
<h4 id="browser"></h4> <table id="results" class="table"></table>
JavaScript 代碼
function () {
//準(zhǔn)備測試數(shù)據(jù), 有200萬條數(shù)據(jù)的大數(shù)組
var testArray = []
, testObject = {}
, idx
, len = 2000000
, tmp = 0
, $results = $("#results")
, $browser = $("#browser")
;
$browser.html(navigator.userAgent);
$results.html('');
for (var i = 0; i < len; i++) {
var number = Math.random(); //若希望加快運(yùn)算速度可使用取整:Math.random() * 10 | 0
testArray.push(number);
testObject[i] = number;
}
$results.append('<tr><th>測試代碼</th><th>計算結(jié)果</th><th>所需時間,毫秒</th></tr>');
//測試函數(shù)
var test = function(testFunc) {
var startTime
, endTime
, result
;
startTime = new Date();
tmp = 0;
testFunc();
endTime = new Date();
//計算測試用例(Test Case)運(yùn)行所需要的時間
result = endTime - startTime;
$results.append('<tr><td><pre>{0}</pre></td><td>{1}</td><td>{2}</td></tr>'.format(testFunc.toString(), tmp | 0, result));
};
test(function() {
//測試for in 的效率
for (idx in testArray) {
tmp += testArray[idx]; //經(jīng)測試,idx是string類型,可能是慢的原因之一
}
});
test(function() {
//測試for loop循環(huán)的效率
for (idx = 0, len = testArray.length; idx < len; idx++) {
tmp += testArray[idx];
}
});
test(function() {
//測試forEach的效率
testArray.forEach(function(data) {
tmp += data;
});
});
test(function() {
//測試不緩存Array.length時效率
for (idx = 0; idx < testArray.length; idx++) {
tmp += testArray[idx];
}
});
test(function() {
//測試使用{} (Object) 存健值對時,使用for in的效率如何
for (idx in testObject) {
tmp += testObject[idx];
}
});
test(function() {
//測試從{} Object查值時的效率如何(這里的健key值事先己知)
for (idx = 0, len = testArray.length; idx < len; idx++) {
tmp += testObject[idx];
}
});
}
運(yùn)行 [需稍等片刻]
測試結(jié)果
測試結(jié)果可能因計算而異,這是在我機(jī)器上運(yùn)行用,F(xiàn)irefox, Chrome, IE三者測試結(jié)果拼接的一張匯總。

以下是幾個觀察到的結(jié)論
- for in比for loop慢非常多,在Chrome中至少慢20倍
- FF對forEach(ES5)做了優(yōu)化,性能比for loop還要好一點,但Chrome/IEn性能均較差
- FF/Chrome緩存Array.length均比直接用時要慢一點。除IE最新版緩存后性能提升微乎其微(這一點非常意外)
- 在某些情況下,F(xiàn)F的JS引擎性能似乎比V8要好些
相關(guān)文章
利用Javascript仿Excel的數(shù)據(jù)透視分析功能
這篇文章給大家介紹了如何利用Javascript實現(xiàn)類似Excel的數(shù)據(jù)透視分析功能,感興趣的朋友們可以參考借鑒,下面來一起看看吧。2016-09-09
微信小程序前后端數(shù)據(jù)交互的詳細(xì)圖文教程
這篇文章主要給大家介紹了關(guān)于微信小程序前后端數(shù)據(jù)交互的相關(guān)資料,通過小程序向后端發(fā)送請求,然后后端從數(shù)據(jù)庫獲取車源和求購的數(shù)量反饋給小程序,最后將這兩個數(shù)據(jù)顯示出來,需要的朋友可以參考下2022-10-10
JavaScript圣杯布局與雙飛翼布局實現(xiàn)案例詳解
這篇文章主要介紹了JavaScript圣杯布局與雙飛翼布局實現(xiàn)案例,這是前端面試中需要掌握的知識點,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08

