每次運行循環體時都會產生性能開銷,增加總的運行時間,即使是循環體中最快的代碼,累計迭代上千次,也將帶來不小的負擔。因此,減少循環的迭代次數可獲得顯著的性能提升。例如:
var iterations=Math.floor(items.length/8),startAt=items.length%8,i=0;
do{
switch(startAt){
case 0:
process(items[i++]);
case 7:
process(items[i++]);
case 6:
process(items[i++]);
case 5:
process(items[i++]);
case 4:
process(items[i++]);
case 3:
process(items[i++]);
case 2:
process(items[i++]);
case 1:
process(items[i++]);
}
startAt=0;
}while(--iterations);
在上面代碼中,每次循環最多可調用process函數8次。循環迭代次數為元素總數除以8。因為總數不一定是8的整數倍,所以startAt變量存放餘數,指明第一次循環中應當執行多少次process。如果現在有12個元素,那麼第一次循環將調用process4次,第二次循環調用process8次,用兩次循環代替了12次循環。在下面的代碼中取消switch表達式,將餘數處理與主循環分開。
var i=items.length%8;
while(i){
process(items[i--]);
}
i=Math.floor(items.length/8);
while(i){
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
}
雖然上面代碼使用兩個循環替代了先前的一個循環,但是在循環體中去掉了switch表達式,速度更快。如果循環的迭代次數少於1000次,減少迭代次數的循環與普通循環相比可能只有微不足道的性能提升。如果迭代次數超過1000次,比如在500 000次迭代中,合理地減少循環的迭代次數可以使運行時間減少到普通循環的70%。
有兩個因素影響到循環的性能:
❑每次迭代幹什麼。
❑迭代的次數。
通過減少這兩者中的一個或全部(的執行時間),可以提高循環的整體性能。如果一次循環需要較長時間來執行,那麼多次循環將需要更長時間。限制在循環體內進行耗時操作的數量是一個加快循環的好方法。一個典型的數組處理循環可採用3種循環的任何一種。最常用的代碼如下:
//方法1
for(var i=0;i<items.length;i++){
process(items[i]);
}
//方法2
var j=0;
while(j<items.length){
process(items[j++]);
}
//方法3
var k=0;
do{
process(items[k++]);
}while(k<items.length);
在每個循環中,每次運行循環體都要發生如下操作:
第1步,在控制條件中讀一次屬性(items.length)。
第2步,在控制條件中執行一次比較(i<items.length)。
第3步,比較操作,觀察條件控制體的運算結果是不是true(i<items.length==true)。
第4步,一次自加操作(i++)。
第5步,一次數組查找(items[i])。
第6步,一次函數調用(process(items[i]))。
在這些簡單的循環中,即使沒有太多的代碼,每次迭代都要進行這6步操作。代碼運行速度很大程度上由process對每個項目的操作所決定,即便如此,減少每次迭代中操作的總數也可以大幅度提高循環的整體性能。
優化循環的第一步是減少對像成員和數組項查找的次數。在大多數瀏覽器上,這些操作比訪問局部變量或直接量需要更長時間。例如,在上面代碼中,每次循環都查找items.length,這是一種浪費,因為該值在循環體執行過程中不會改變,因此產生了不必要的性能損失。我們可以簡單地將此值存入一個局部變量中,在控制條件中使用這個局部變量,從而提高了循環性能,例如:
for(var i=0,len=items.length;i<len;i++){
process(items[i]);
}
var j=0,count=items.length;
while(j<count){
process(items[j++]);
}
var k=0,num=items.length;
do{
process(items[k++]);
}while(k<num);
這些重寫後的循環只在循環執行之前對數組長度進行一次屬性查詢,使控制條件中只有局部變量參與運算,因此速度更快。根據數組的長度,在大多數瀏覽器上總循環時間可以節省大約25%,在IE瀏覽器中可節省50%。
還可以通過改變循環的順序來提高循環性能。通常,數組元素的處理順序與任務無關,可以從最後一個開始,直到處理完第一個元素。倒序循環是編程語言中常用的性能優化方法,不過一般不太容易理解。在JavaScript中,倒序循環可以略微提高循環性能,例如:
for(var i=items.length;i--;){
process(items[i]);
}
var j=items.length;
while(j--){
process(items[j]);
}
var k=items.length-1;
do{
process(items[k]);
}while(k--);
在上面代碼中使用了倒序循環,並且在控制條件中使用了減法。每個控制條件只是簡單地與零進行比較。控制條件與true值進行比較,任何非零數字自動強制轉換為true,而零等同於false。實際上,控制條件已經從兩次比較減少到一次比較。將每次迭代中兩次比較減少到一次可以大幅度提高循環速度。通過倒序循環和最小化屬性查詢,可以看到執行速度比原始版本提升了50%~60%。
與原始版本相比,每次迭代中只進行如下操作:
第1步,在控制條件中進行一次比較(i==true)。
第2步,一次減法操作(i--)。
第3步,一次數組查詢(items[i])。
第4步,一次函數調用(process(items[i]))。
新循環的每次迭代中減少兩個操作,隨著迭代次數的增長,性能將顯著提升。