我們知道遍歷數組的方式之一就是 使用for循環來遍歷,如:
var arr = [1, 2, 3, 4, 5, 6];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]); //1,2,3,4,5,6
}
但是這樣感覺代碼有點多,不簡潔,因此ES5發佈之後,可以使用數組的forEach()迭代方法來遍歷數組:
forEach()方法:爲數組的第一項給定函數,對數組的第一項運行給定的函數,該函數沒有返回值。相當於遍歷數組。
var arr = [1, 2, 3, 4, 5, 6];
arr.forEach(function (value) {
console.log(value); //1,2,3,4,5,6
});
這樣代碼更加就簡潔,但有一個小小的缺陷:就是不能使用break、continue語句等來中斷函數,也不能使用return語句返回到外層函數。
當然除了以上兩種方法遍歷數組外,還可以使用for-in枚舉方法來遍歷數組:
var arr = [1, 2, 3, 4, 5, 6];
for (var i in arr) {
console.log(i); //0,1,2,3,4,5 索引
console.log(arr[i]); //1,2,3,4,5,6 數組項的值
console.log(typeof(i)); //string,string,string,string,string,string 字符串
}
但是這樣做是一個很糟糕的選擇,爲什麼呢?
- 在這段代碼中,賦給i的不是實際的值,而是字符串"0","1","2"等(遍歷到字符串類型的鍵--遍歷普通對象的),表示的是數組項在數組中的位置索引,這很可能在無意之間進行字符串計算,如:"2" + 1 = "21"等情況,所以會帶來不必要的麻煩。
- for-in除了可以遍歷數組外,我們還知道可以遍歷自定義屬性,也就是可以枚舉 可枚舉屬性,那麼在遍歷數組時,它還額外執行一次,用於遍歷可枚舉屬性了,這是我們不需要的操作。甚至還可以遍歷到數組原型鏈上的屬性,這更是一個大麻煩。
- for-in循環還有一個更大的缺點:那就是它會以隨機次序遍歷數組元素,這更是我們不想要的。也就是說它遍歷出的元素的順序不是數組中真正的順序。
- for-in循環主要用於遍歷普通對象的,用於遍歷字符串類型的鍵,也就是對象的屬性名(鍵名)。不適用於遍歷數組。
以上所列舉的方法可以都有缺點,那麼用什麼方法來遍歷數組更好呢?
for-of循環
在ES6中,有一個新的特性for-of循環可以用來遍歷數組:
var arr = [1, 2, 3, 4, 5, 6];
for (var i of arr) {
console.log(i); //1,2,3,4,5,6
}
for-of循環的優點:
- 這是一種更簡潔、更直接用於遍歷數組的方法
- 這個方法避開了for-in循環的所有缺點。
- 與forEach()不同的是,for-of循環可以使用break、continue、return等語句來中斷函數的運行。
- for-in循環主要用於對象屬性的遍歷。
- for-of循環用於遍歷數據--如對數組元素的遍歷
for -of對其它集合的遍歷
for-of循環不僅用於數組,還可以用於數組對象等,如DOM中的nodeList等,可以用來遍歷DOM中的子節點、根據給定條件返回的元素節點的集合等。
for-of循環還可以用於遍歷字符串,如:
var str = "hello ES6";
for (var i of str) {
console.log(i); //h e l l o E S 6
}
同樣,for-of循環還支持對Map和Set對象的遍歷,這兩個對象到後面會一一講解。
未來JavaScript還有許多的新的集合類型出現,那麼for-of循環就是用來遍歷這些新的集合類型的。
for-of循環不支持對普通對象的遍歷,如果想遍歷普通對象,可以使用for-in循環,for-in循環主要是遍歷對象的屬性的(鍵/值)。
深入理解
for-of循環語句通過 方法調用 來遍歷各種集合。 我們所討論的數組、Map、Set對象以及其它對象有一個共同點:它們都有一個迭代器方法。
我們可以爲任何類型的對象添加迭代器方法。
當我們向任意對象添加[Symbol.iterator]()方法後,就可以遍歷這個對象了。
Symbol是ES6中的一個新特性,所有擁有[Symbol.iterator]()方法的對象被稱爲 可迭代的。
迭代器對象
for-of循環的機制
for-of循環首先會調用 [Symbol.iterator]()方法,並返回一個 迭代對象,迭代對象可以是任意有.next()方法的對象,for-of循環會重複調用next()方法,循環一次調用一次。
一個最簡單的迭代器:
var ydq = {
[Symbol.iterator]: function () {
return this; //返回新的迭代器對象 ,就是返回ydq對象(this)。
},
next: function () {
//返回給for-of循環的結果有兩種可能。
return {done: false, value: 0};
}
};
每一次調用next()方法,它都會返回相同的結果,返回給 for-of循環的結果 有兩種可能:(a)沒有完成迭代,(b)下一個值爲0。
我們簡單瞭解了一下for-of循環的機制,現在寫一個簡單的for-of循環,按下列方法調用重寫被迭代的對象。
首先是for-of循環:
for (var i of obj) {
//一些語句
}
接着,使用下列方法和部分臨時變量實現一個與前面相似的例子:
//調用[Symbol.ietrator]()方法,並返回新迭代對象$ietrator
var $ietrator = obj[Symbol.ietrator]();
//接着新迭代對象調用next()方法並返回dome,value屬性值
var $next = $ietrator.next();
//確定dome、value屬性值
while (!$next.dome) {
i = $next.value; //將value屬性值賦值給i。
//一些語句
$next = $ietrator.next(); //並繼續調用next()方法。
}
經過以上步驟就可以將集合中的數據一一遍歷出來。
當沒有完成迭代時(dome: false)才繼續迭代下去,並返回value屬性的值給i。