亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

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、continuereturn 配合使用;
  • 提供了遍歷所有數據結構的統一操作接口。
for (let n of fib) {
  if (n > 520)
    break;
  console.log(n);
}

當迭代項大于 520 時 break 語句會跳出 for...of 循環。

4. 小結

  • 對于數組的處理盡量使用 for...of 去迭代數據;
  • 如果要遍歷的是對象,并且沒有順序的限制可以使用 for...in 方式遍歷對象更好的處理數據。