在JavaScript剛剛開始萌生的時候,我們通常使用這種方式遍歷數組
let arr1 = [1, 2, 3, 4];
for (let index = 0; index < arr1.length; index++) {
const element = arr1[index];
console.log(element); // 1 2 3 4
}
自從ES5 正式發布之后,我們可以使用 forEach 方法來遍歷數組.
let arr2 = [1, 2, 3, 4];
arr2.forEach( item => {
if (item === 3) return
console.log(item); // 1 2 4
})
- 顯然的,這段代碼看起來更加簡潔,但是并沒有按照我們預期的進行終結遍歷。
- 在文檔中我們可以看到描述
forEach() 方法對數組的每個元素執行一次提供的函數。
意思就是即使你在某個函數中return
了也只是終結了當次函數的執行。
那么,你一定想嘗試一下 for-in 循環
let arr3 = [1, 2, 3, 4];
for (const index in arr3) {
if (index === 3) break
console.log(index); // 0 1 2 3
console.log(typeof index); // string
}
程序再次沒有按照我們預期的運行:
在這段代碼中,賦值給index的值并不是實際的數字,可以看到輸出的是string '0' '1' '2' '3', 因此如果在這之前你并沒深入理解,可能會掉進坑里,此時我們可以把 === 改成 == 即可
for...in 循環只遍歷可枚舉屬性。像Array和Object使用內置構造函數所創建的對象都會繼承自Object.prototype 和 String.prototype的不可枚舉屬性, 如果在原型上自定義了屬性,那么稍微不注意將會導致如下錯誤:
let arr3 = [1, 2, 3, 4];
Array.prototype.name = '看看我被遍歷了沒';
for (const key in arr3) {
// 將額外的遍歷屬性
// const element = arr3[key];
// console.log(element); // 1 2 3 4 '看看我被遍歷沒'
// 正確做法
if (arr3.hasOwnProperty(key)) {
const element = arr3[key];
console.log(element); // 1 2 3 4
}
}
更加可怕的是,由于迭代的順序是依賴于執行環境的,所以數組遍歷不一定按次序訪問元素。
簡而言之, for-in 是為普通對象設計的,你可以遍歷得到字符串類型的鍵,因此不適用于數組遍歷。
更好的選擇 for-of 循環
在ES6中增加了一種新的循環語法來解決目前的問題:
for (variable of iterable) {
//statements
}
for...of 可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上創建一個迭代循環,調用自定義迭代鉤子,并為每個不同屬性的值執行語句。
這是最簡潔、最直接的遍歷數組元素的語法
這個方法避開了 for-in 循環的所有缺陷
與forEach不同的是,它可以正確響應 break、continue 和 return 語句
未來的 JS 可以使用一些新型的集合類,甚至會有更多的類型
陸續誕生,而 for-of 就是為遍歷所有這些集合特別設計的循環語句for-of 循環不支持普通對象,但如果你想迭代一個對象的屬性,你可以用 for-in 循環(這也是它的本職工作)或內建的 Object.keys()方法:
// 向控制臺輸出對象的可枚舉屬性
for (var key of Object.keys(someObject)) {
console.log(key + ": " + someObject[key]);
}
深入理解
“能工摹形,巧匠竊意.” -- 巴勃羅·畢加索
ES6始終堅信這樣的宗旨: 凡是新加入的特性,勢必已在其他語言中得到強有力的實用性證明。
舉個例子,新加入的for-of 循環像極了C++、Java、C#以及Python中的循環語句。與他們一樣,這里的 for-of 循環支持語言和標準庫中提供的幾種不同的數據結構,它同樣也是這門語言中的一個擴展點。