首頁  >  文章  >  web前端  >  深入探究JavaScript中for迴圈的效率問題及相關優化_javascript技巧

深入探究JavaScript中for迴圈的效率問題及相關優化_javascript技巧

WBOY
WBOY原創
2016-05-16 15:10:521543瀏覽

Underscore.js庫

你一天(一週)內寫了幾個循環了?

var i;
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 doSomeWorkOn(someThing);
}

這當然無害,但這種寫法非常醜而且奇怪,這也不是真正需要抱怨的。但這種寫法太平庸了。

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]);
 }
}

你在擴充糟糕的程式碼,在你拋出一大堆if前,你已經精神錯亂了。
我在兩年裡沒有寫一個迴圈(loop)。
「你在說什麼?」
這是真的,一個冷笑話。其實不是一個都沒有(好吧,我確實寫了幾個),因為我不寫循環(loops),我的程式碼比較容易理解。
怎麼做的呢?

_.each(someArray, function(someThing) {
 doSomeWorkOn(someThing);
})

或更好一點:

_.each(someArray, doSomeWorkOn);

這就是underscorejs所做到的。乾淨,簡單,易讀,短,沒有中間變量,沒有成堆的分號,簡單非常優雅。
這是另外一些例子。

var i,
 result = [];
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 // 打到这,我已经手疼了
 if(someThing.isAwesome === true) {
   result.push(someArray[i]);
 }
}

同樣,一個使用循環浪費時間的典型用例。即便這些網站是宣傳禁煙和素食主義的,看到這些程式碼我也感到義憤。看看簡單的寫法。

var result = _.filter(someArray, function(someThing) {
 return someThing.isAwesome === true;
})

像underscore中的filter(過濾)的名字一樣,隨手寫的3行程式碼就可以給你一個新的陣列(array)。
還是你想把這些陣列轉換成另外一種形式?

var result = _.map(someArray, function(someThing) {
 return trasformTheThing(someThing);
})

上面三個例子在日常生活中已經夠用了,但這些功能還不足矣讓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)

這個剛開始看起來可能有點怪,我查了下關於reduce的文檔,知道了它的存在。因為我拒絕使用循環,所以它是我的首選。上面這些東西只是入門,underscorejs庫還有一大堆牛B的功能。

30天不使用循環的挑戰。

在一下一個30天裡,不要使用任何循環,如果你看到一堆討厭和粗糙的東西,用each或map將他們替換掉。再用一點reducing。

你需要注意到,Underscore是通往函數式程式設計的。一種看得見,看不見的方式。一條很好的途徑。


OurJS附註*目前現代瀏覽器已經支援each, filter, map, reduce方法,但underscore庫可以實現對舊版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 个循环: 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(循環)的效率低非常多。現在我們測試一下在不同瀏覽器中使用for in, for loop和forEach在處理大數組時的效率究竟如何。

目前絕大部分開源軟體都會在for loop中快取數組長度,因為普通觀點認為某些瀏覽器Array.length每次都會重新計算數組長度,因此通常用臨時變數來事先儲存數組長度,如:

for (var idx = 0, len = testArray.length; idx < len; idx++) {
 //do sth.
}

我們也會測試一下快取與不快取時的效能差異。

同時在每個測試循環中加入求和運算,來表示其不是空循環。

for (var idx = 0, len = testArray.length; idx < len; idx++) {
 //do sth.
}

我們也會測試一下快取與不快取時的效能差異。

同時在每個測試循環中加入求和運算,來表示其不是空循環。

測試程式碼如下,點擊運行即可查看
HTML 程式碼

<h4 id="browser"></h4>
<table id="results" class="table"></table>

JavaScript 程式碼

function () {

 //准备测试数据, 有200万条数据的大数组
 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(); //若希望加快运算速度可使用取整:Math.random() * 10 | 0
  testArray.push(number);
  testObject[i] = number;
 }

 $results.append('<tr><th>测试代码</th><th>计算结果</th><th>所需时间,毫秒</th></tr>');

 //测试函数
 var test = function(testFunc) {
  var startTime
   , endTime
   , result
   ;

  startTime = new Date();
  tmp = 0;
  testFunc();
  endTime  = new Date();

  //计算测试用例(Test Case)运行所需要的时间
  result = endTime - startTime;
  $results.append('<tr><td><pre class="brush:php;toolbar:false">{0}
{1}{2}'.format(testFunc.toString(), tmp | 0, result)); }; test(function() { //测试for in 的效率 for (idx in testArray) { tmp += testArray[idx]; //经测试,idx是string类型,可能是慢的原因之一 } }); test(function() { //测试for loop循环的效率 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]; } }); }

運行 [需等待片刻]
測試結果
測試結果可能因計算而異,這是在我機器上運行用,Firefox, Chrome, IE三者測試結果拼接的一張總結。

2016313110044207.jpg (971×678)

以下是幾個觀察到的結論

  • for in比for loop慢非常多,在Chrome中至少慢20倍
  • FF對forEach(ES5)做了最佳化,效能比for loop好一點,但Chrome/IEn效能都差
  • FF/Chrome快取Array.length均比直接用時慢一點。除IE最新版快取後效能提升微乎其微(這點非常意外)
  • 在某些情況下,FF的JS引擎性能似乎比V8好
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn