for…of
1. 前言
在編程中最常見的就是對數(shù)據(jù)的遍歷操作,ES5 有針對數(shù)組和對象的遍歷方法,但這些方法或多或少地都會(huì)存在一定的問題。為了統(tǒng)一解決這些問題,ES6 給出了終極的解決方案 ——for...of
。
for...of
對于可迭代的對象(包括:內(nèi)置的 String、Array、類似數(shù)組的對象(arguments 或 NodeList)、TypedArray、Map、Set,和用戶定義的可迭代對象等)上創(chuàng)建一個(gè)迭代循環(huán),它不局限于數(shù)組下的循環(huán),只要是可迭代的對象就可以被 for...of
進(jìn)行循環(huán)。
2. 基本語法
2.1 語法使用
for (const iterator of iterable) {
// 執(zhí)行語句
}
參數(shù)解釋:
參數(shù) | 描述 |
---|---|
iterator |
在每次迭代中,將不同屬性的值分配給變量,用于循環(huán)中語句的使用; |
iterable |
被迭代枚舉的對象。 |
2.2 迭代 Array
for...of
最常用的場景就是對數(shù)組的迭代,也是取代 for
、forEach
的最好選擇。
let arr = [10, 20, 30];
for (let value of arr) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
上面的代碼中對 value 值進(jìn)行加 1 操作,如果 value 值不能被修改,也可以使用 const
來定義 value。
2.3 迭代字符串
for...of
可以迭代字符串,迭代后的結(jié)果是把字符進(jìn)行分割,得到每個(gè)單個(gè)字符。
let str = '慕課';
for (let value of str) {
console.log(value);
}
// 慕
// 課
2.4 迭代 TypedArray
let iterable = new Uint8Array([0x00, 0xff]);
for (let value of iterable) {
console.log(value);
}
// 0
// 255
2.5 迭代 Set 和 Map
在 Set 和 Map 章節(jié)中我們就說到了,Set 和 Map 可以使用 for...of
來進(jìn)行循環(huán),主要因?yàn)?Set 和 Map 具有可迭代屬性。
let setArr = new Set([1, 1, 2, 2, 3, 3]);
for (let value of setArr) {
console.log(value);
}
// 1
// 2
// 3
上面的代碼需要注意的是,迭代的是 new Set()
后的結(jié)果,new Set()
會(huì)對數(shù)組進(jìn)行去重操作,所以得到以上結(jié)果。
let map = new Map([["a", 1], ["b", 2], ["c", 3]]);
for (let value of map) {
console.log(value);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]
上面的代碼中使用 new Map()
傳入一個(gè)二維數(shù)組,這里需要注意的是,迭代的結(jié)果是一個(gè)帶有 key 和 value 的數(shù)組,所以也可以用數(shù)組解構(gòu)的方式 把 key 和 value 的值取出來,直接使用:
for (let [key, value] of map) {
console.log(key, value);
}
// a 1
// b 2
// c 3
2.6 迭代類數(shù)組對象
1. 迭代 argument 對象
我們知道在函數(shù)中可以使用 Argument
對象拿到在調(diào)用函數(shù)時(shí)拿到傳遞的參數(shù),因?yàn)?arguments
不是一個(gè) Array
,它屬于類數(shù)組,可以借助 call
來得到一個(gè)數(shù)組。[].slice.call(arguments)
, 而使用 for...of
可以直接對 arguments
循環(huán),得到的結(jié)果也只是傳入的參數(shù)。這個(gè)可以很方便地去循環(huán)類數(shù)組對象。
function argfn() {
for (let argument of arguments) {
console.log(argument);
}
}
argfn(1,2,3)
// 1
// 2
// 3
上面的代碼可以看出來,打印的結(jié)果只有 1、2、3
沒有類數(shù)組上的其他屬性值。
2. 迭代 DOM 集合
其實(shí)最常見的數(shù)組對象是得到網(wǎng)頁上的 DOM 元素的集合,它也是一個(gè)類數(shù)組對象。比如一個(gè) NodeList 對象:下面的例子演示給每一個(gè) p 標(biāo)簽添加一個(gè) “read” 類。
//注意:這只能在實(shí)現(xiàn)了NodeList.prototype[Symbol.iterator]的平臺(tái)上運(yùn)行
let prags = document.querySelectorAll("p");
for (let value of prags) {
value.classList.add("read");
}
上面的代碼,需要在在帶有 p 的標(biāo)簽的 html 文件中運(yùn)行。
3 知識(shí)對比
ES5 中提供了很多遍歷的方法,下面我們與之一一對比看看 for...of
有什么優(yōu)勢。
3.1 對比 for
最原始的語法是 for
循環(huán)語句,但是這種寫法比較麻煩,每個(gè)步驟的信息都需要手動(dòng)地去處理。
const fib = [1,1,2,3,5,8,13...]; // 斐波那切數(shù)列
for (let index = 0; index < fib.length; index++) {
console.log(fib[index]);
}
3.2 對比 forEach
數(shù)組中內(nèi)置了 forEach
方法,這個(gè)方法的致命缺點(diǎn)就是不能跳出循環(huán),break
命令和 return
命令都不能奏效。
fib.forEach((value) => {
console.log(value);
});
3.3 對比 for…in
for...in
用以遍歷對象的屬性,for...of
用以遍歷數(shù)據(jù),就像數(shù)組中的值一樣;for...in
主要是針對對象循環(huán)而設(shè)計(jì)的,對于數(shù)組,鍵就是數(shù)字,但是在for...in
循環(huán)中是以字符串作為鍵名;for...in
循環(huán)不僅遍歷數(shù)字鍵名,還會(huì)遍歷手動(dòng)添加的其他鍵,甚至包括原型鏈上的鍵;- 某些情況下,
for...in
循環(huán)以任意順序遍歷鍵名,主要是因?yàn)閷ο笤趦?nèi)存中的數(shù)據(jù)類型決定的。
for (let index in fib) {
console.log(fib[index]);
}
3.4 for...of
的優(yōu)點(diǎn)
- 有著同
for...in
一樣的簡潔語法,但是沒有for...in
那些缺點(diǎn); - 不同于
forEach
方法,它可以與break
、continue
和return
配合使用; - 提供了遍歷所有數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一操作接口。
for (let n of fib) {
if (n > 520)
break;
console.log(n);
}
當(dāng)?shù)?xiàng)大于 520 時(shí) break
語句會(huì)跳出 for...of
循環(huán)。
4. 小結(jié)
- 對于數(shù)組的處理盡量使用
for...of
去迭代數(shù)據(jù); - 如果要遍歷的是對象,并且沒有順序的限制可以使用
for...in
方式遍歷對象更好的處理數(shù)據(jù)。