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