for...of你讓for...in,forEach情何以堪

歡迎大家的叫交流學習
在這裏插入圖片描述

for…of

for…of語句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上創建一個迭代循環,調用自定義迭代鉤子,併爲每個不同屬性的值執行語句

const array1 = ['a', 'b', 'c'];

for (const element of array1) {
  console.log(element);
}

// expected output: "a"
// expected output: "b"
// expected output: "c"

一、語法

for (variable of iterable) {
    //statements
}
  • variable
    在每次迭代中,將不同屬性的值分配給變量。
  • iterable
    被迭代枚舉其屬性的對象。

二、示例

1. 迭代Array

數組原生具備iterator接口(即默認部署了Symbol.iterator屬性),for…of循環本質上就是調用這個接口產生的遍歷器,可以用下面的代碼證明。

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
  console.log(v); // red green blue
}

const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);

for(let v of obj) {
  console.log(v); // red green blue
}

上面代碼中,空對象obj部署了數組arr的Symbol.iterator屬性,結果obj的for…of循環,產生了與arr完全一樣的結果。
for…of循環可以代替數組實例的forEach方法。

const arr = ['red', 'green', 'blue'];

arr.forEach(function (element, index) {
  console.log(element); // red green blue
  console.log(index);   // 0 1 2
});

JavaScript 原有的for…in循環,只能獲得對象的鍵名,不能直接獲取鍵值。ES6 提供for…of循環,允許遍歷獲得鍵值。

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}

上面代碼表明,for…in循環讀取鍵名,for…of循環讀取鍵值。如果要通過for…of循環,獲取數組的索引,可以藉助數組實例的entries方法和keys方法(參見《數組的擴展》一章)。
for…of循環調用遍歷器接口,數組的遍歷器接口只返回具有數字索引的屬性。這一點跟for…in循環也不一樣。

let iterable = [10, 20, 30];

for (let value of iterable) {
    value += 1;
    console.log(value);
}
// 11
// 21
// 31

如果你不想修改語句塊中的變量 , 也可以使用const代替let。

let iterable = [10, 20, 30];

for (const value of iterable) {
  console.log(value);
}
// 10
// 20
// 30

2. 迭代String

let iterable = "boo";

for (let value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"

3. 迭代 TypedArray

let iterable = new Uint8Array([0x00, 0xff]);

for (let value of iterable) {
  console.log(value);
}
// 0

// 255

4. 迭代Map

let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let entry of iterable) {
  console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]

for (let [key, value] of iterable) {
  console.log(value);
}
// 1
// 2
// 3

5. 迭代 Set

let iterable = new Set([1, 1, 2, 2, 3, 3]);

for (let value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

6. 迭代 arguments 對象

(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);

// 1
// 2
// 3

7. 迭代 DOM 集合

迭代 DOM 元素集合,比如一個NodeList對象:下面的例子演示給每一個 article 標籤內的 p 標籤添加一個 “read” 類。

//注意:這只能在實現了NodeList.prototype[Symbol.iterator]的平臺上運行
let articleParagraphs = document.querySelectorAll("article > p");

for (let paragraph of articleParagraphs) {
  paragraph.classList.add("read");
}

8. 關閉迭代器

對於for…of的循環,可以由break, throw continue 或return終止。在這些情況下,迭代器關閉。

function* foo(){ 
  yield 1; 
  yield 2; 
  yield 3; 
}; 

for (let o of foo()) { 
  console.log(o); 
  break; // closes iterator, triggers return
}

9. 迭代生成器

你還可以迭代一個生成器:

function* fibonacci() { // 一個生成器函數
    let [prev, curr] = [0, 1];
    for (;;) { // while (true) {
        [prev, curr] = [curr, prev + curr];
        yield curr;
    }
}
 
for (let n of fibonacci()) {
     console.log(n); 
    // 當n大於1000時跳出循環
    if (n >= 1000)
        break;
}

10. 不要重用生成器

生成器不應該重用,即使for…of循環的提前終止,例如通過break關鍵字。在退出循環後,生成器關閉,並嘗試再次迭代,不會產生任何進一步的結果。

var gen = (function *(){
    yield 1;
    yield 2;
    yield 3;
})();
for (let o of gen) {
    console.log(o);
    break;//關閉生成器
} 

//生成器不應該重用,以下沒有意義!
for (let o of gen) {
    console.log(o);
}

11. 迭代其他可迭代對象

你還可以迭代顯式實現可迭代協議的對象:

var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (var value of iterable) {
  console.log(value);
}
// 0
// 1
// 2

三、 for…of與for…in的區別

無論是for…in還是for…of語句都是迭代一些東西。它們之間的主要區別在於它們的迭代方式。

for…in 語句以任意順序迭代對象的可枚舉屬性。

for…of 語句遍歷可迭代對象定義要迭代的數據。

以下示例顯示了與Array一起使用時,for…of循環和for…in循環之間的區別。

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // logs 0, 1, 2, "foo"
  }
}

for (let i of iterable) {
  console.log(i); // logs 3, 5, 7
}
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {}; 

let iterable = [3, 5, 7]; 
iterable.foo = 'hello';

每個對象將繼承objCustom屬性,並且作爲Array的每個對象將繼承arrCustom屬性,因爲將這些屬性添加到Object.prototype和Array.prototype。由於繼承和原型鏈,對象iterable繼承屬性objCustom和arrCustom。

for (let i in iterable) {
  console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom" 
}

此循環僅以原始插入順序記錄iterable 對象的可枚舉屬性。它不記錄數組元素3, 5, 7 或hello,因爲這些不是枚舉屬性。但是它記錄了數組索引以及arrCustom和objCustom。如果你不知道爲什麼這些屬性被迭代,array iteration and for…in中有更多解釋。

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // logs 0, 1, 2, "foo"
  }
}

這個循環類似於第一個,但是它使用hasOwnProperty() 來檢查,如果找到的枚舉屬性是對象自己的(不是繼承的)。如果是,該屬性被記錄。記錄的屬性是0, 1, 2和foo,因爲它們是自身的屬性(不是繼承的)。屬性arrCustom和objCustom不會被記錄,因爲它們是繼承的。

for (let i of iterable) {
  console.log(i); // logs 3, 5, 7 
}

該循環迭代並記錄iterable作爲可迭代對象定義的迭代值,這些是數組元素 3, 5, 7,而不是任何對象的屬性。

四、規範

在這裏插入圖片描述

五、後記

喜歡我的可以關注我哦,相互學習。在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章