JS中for...in循環陷阱及遍歷數組的方式對比


JavaScript中有很多遍歷數組的方式,比較常見的是for(var i=0;i<arr.length;i++){},以及for...in...循環等,這些遍歷都有各自的優缺點,下面來看看各種JS的遍歷對比:

1.for...in...

1).index索引為字符串型數字,不能直接進行幾何運算。

2).遍歷順序有可能不是按照實際數組的內部順序。

3).使用for in會遍歷數組所有的可枚舉屬性,包括原型。例如上栗的原型方法method和name屬性。

Array.prototype.myfun=function(){
    alert('myfun');
}    
var arr = [0,1,2,3];

for(var i in arr){
    console.log(arr[i]);
}

console.log(Array.prototype)

 運行結果:

上面的例子很好的反映了for...in...循環的缺點,原本只想循環取出該數組的數據,但是由於之前給數組添加了原型函數,導致循環的結果多了一個函數。這種場景我在使用lodop打印控件寫打印功能時遇到過,打印的內容我用for...in...循環打印了,結果打印的結果莫名其妙多了一串代碼,排查了很久才發現原來是lodop.js將Array數組的原型添加了方法,導致循環的時候將該方法也遍歷到了,所以for...in...循環最好使用在遍歷對象上,不要用來遍歷數組。

 

2.普通for循環

let arr = [1,2,3,4,5]; 

for (var index = 0; index < arr.length; index++) {
 console.log(arr[index]); 
}

這是最原始的寫法,也是應用比較多的循環方式,但寫法較為麻煩,性能有優化空間。

 

3.優化for循環

var arr = [0,1,2,3];

for(j=0,len=arr.length;j<len;j++){
    console.log(arr[j]);
}

使用臨時變量,將長度緩存起來,避免重復獲取數組長度,當數組較大時優化效果才會比較明顯。這種方法基本上是所有循環遍歷方法中性能最高的一種,所以推薦使用這種循環。

 

4.forEach循環

let arr = [1,2,3,4,5]; 
arr.forEach((element,index) => { 
    console.log(element); 
});

forEach循環是數組自帶的方法,這種方式循環十分方便,不需要獲取數組長度即可循環,但是有一個問題,在循環的中途無法跳出forEach循環,break命令或return命令都不能奏效,也就是說一循環必然會執行完畢,不能靈活運用於多種情況。

 

5.for...of...循環

for…of是ES6新增的遍歷方式,它提供了統一的遍歷機制。所有實現了[Symbol.iterator]接口的對象都可以被遍歷。for...of循環可以使用的范圍包括數組、Set 和 Map 結構、某些類似數組的對象(比如arguments對象、DOM NodeList 對象)、Generator 對象,以及字符串。

優點:

  • 有着同for...in一樣的簡潔語法,但是沒有for...in那些缺點
  • 不同用於forEach方法,它可以與breakcontinuereturn配合使用
  • 提供了遍歷所有數據結構的統一操作接口

下面是一個使用break語句,跳出for...of循環的例子。

for (var n of arr) {
 if (n > 2) break;console.log(n); 
}

上面的例子,會輸出斐波納契數列小於等於2的項。如果當前項大於2,就會使用break語句跳出for...of循環。

for...of獲取索引
  • entries返回一個遍歷器對象,用來遍歷[鍵名, 鍵值]組成的數組。對於數組,鍵名就是索引值;對於 Set,鍵名與鍵值相同。Map 結構的 Iterator 接口,默認就是調用entries方法。
  • keys返回一個遍歷器對象,用來遍歷所有的鍵名。
  • values返回一個遍歷器對象,用來遍歷所有的鍵值。
let arr = ['a', 'b', 'c']; 
for (let pair of arr.entries) { 
console.log(pair); 
} // [0, 'a'] // [1, 'b'] // [2, 'c']
類似數組的對象

類似數組的對象包括好幾類。下面是for...of循環用於字符串、DOM NodeList 對象、arguments對象的例子。

// 字符串 
let str = "hello"; 
for (let s of str) {
 console.log(s); // h e l l o 
} // DOM NodeList對象 
let paras = document.querySelectorAll("p");
for (let p of paras) { 
    p.classList.add("test"); 
} // arguments對象 
function printArgs { 
    for (let x of arguments) { 
        console.log(x); 
    } 
} 
printArgs('a', 'b'); // 'a' // 'b'

並不是所有類似數組的對象都具有 Iterator 接口,一個簡便的解決方法,就是使用Array.from方法將其轉為數組。

let arrayLike = { 
    length: 2, 
    0: 'a', 
    1: 'b' 
}; 
// 報錯
for (let x of arrayLike) { 
   console.log(x); 
} 
// 正確 
for (let x of Array.from(arrayLike)) { 
  console.log(x); // 'a' // 'b'
}
普通的對象

對於普通的對象,for...of結構不能直接使用,會報錯,必須部署了 Iterator 接口后才能使用。

let es6 = { 
    edition: 6, 
    committee: "TC39", 
    standard: "ECMA-262" 
}; 
for (let e in es6) { 
    console.log(e); 
} // edition // committee // standard 
for (let e of es6) {
    console.log(e); 
} // TypeError: es6 is not iterable

解決方法是,使用Object.keys方法將對象的鍵名生成一個數組,然后遍歷這個數組。

let es6 = { 
    edition: 6, 
    committee: "TC39", 
    standard: "ECMA-262" 
};
for (var key of Object.keys(es6)) { 
    console.log(key + ': ' + es6[key]); 
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM