1. 程式人生 > >javascript中for of和for in的區別

javascript中for of和for in的區別

https://segmentfault.com/q/1010000006658882

先說結論:

  1. 推薦在迴圈物件屬性的時候,使用for...in,在遍歷陣列的時候的時候使用for...of

  2. for...in迴圈出的是key,for...of迴圈出的是value

  3. 注意,for...of是ES6新引入的特性。修復了ES5引入的for...in的不足

  4. for...of不能迴圈普通的物件,需要通過和Object.keys()搭配使用

假設我們要遍歷一個數組的valuelet aArray = ['a',123,{a:'1',b:'2'}]

使用for...in

迴圈:

for(let index in aArray){
    console.log(`${aArray[index]}`);
}

使用for...of迴圈:

for(var value of aArray){
    console.log(value);
}

咋一看好像好像只是寫法不一樣而已,那為什麼說for...of修復了for...in的缺陷和不足。
假設我們往陣列新增一個屬性name:
aArray.name = 'demo',再分別檢視上面寫的兩個迴圈:

for(let index in aArray){
    console.log(`${aArray[index]}`); //Notice!!aArray.name也被迴圈出來了
}
for(var value of aArray){
    console.log(value);
}

所以說,作用於陣列的for-in迴圈除了遍歷陣列元素以外,還會遍歷自定義屬性。

for...of迴圈不會迴圈物件的key,只會迴圈出陣列的value,因此for...of不能迴圈遍歷普通物件,對普通物件的屬性遍歷推薦使用for...in

 

ECMAScript 6 入門

作者:阮一峰

授權:署名-非商用許可證

 

http://es6.ruanyifeng.com/#docs/iterator

for...of 迴圈

ES6 借鑑 C++、Java、C# 和 Python 語言,引入了for...of迴圈,作為遍歷所有資料結構的統一的方法。

一個數據結構只要部署了Symbol.iterator屬性,就被視為具有 iterator 介面,就可以用for...of迴圈遍歷它的成員。也就是說,for...of迴圈內部呼叫的是資料結構的Symbol.iterator方法。

for...of迴圈可以使用的範圍包括陣列、Set 和 Map 結構、某些類似陣列的物件(比如arguments物件、DOM NodeList 物件)、後文的 Generator 物件,以及字串。

陣列

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

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

for...of迴圈可以代替陣列例項的forEach方法。

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 arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

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

上面程式碼中,for...of迴圈不會返回陣列arrfoo屬性

類似陣列的物件

類似陣列的物件包括好幾類。下面是for...of迴圈用於字串、DOM NodeList 物件、arguments物件的例子。

// 字串
let str = "hello";

for (let s of str) {
  console.log(s); // h e l l o
}

// DOM NodeList物件
let paras = document.querySelectorAll("p");

for (let p of paras) {
  p.classList.add("test");
}

// arguments物件
function printArgs() {
  for (let x of arguments) {
    console.log(x);
  }
}
printArgs('a', 'b');
// 'a'
// 'b'

對於字串來說,for...of迴圈還有一個特點,就是會正確識別 32 位 UTF-16 字元。

for (let x of 'a\uD83D\uDC0A') {
  console.log(x);
}
// 'a'
// '\uD83D\uDC0A'

並不是所有類似陣列的物件都具有 Iterator 介面,一個簡便的解決方法,就是使用Array.from方法將其轉為陣列。

let arrayLike = { length: 2, 0: 'a', 1: 'b' };

// 報錯
for (let x of arrayLike) {
  console.log(x);
}

// 正確
for (let x of Array.from(arrayLike)) {
  console.log(x);
}

物件

對於普通的物件,for...of結構不能直接使用,會報錯,必須部署了 Iterator 介面後才能使用。但是,這樣情況下,for...in迴圈依然可以用來遍歷鍵名。

let es6 = {
  edition: 6,
  committee: "TC39",
  standard: "ECMA-262"
};

for (let e in es6) {
  console.log(e);
}
// edition
// committee
// standard

for (let e of es6) {
  console.log(e);
}
// TypeError: es6[Symbol.iterator] is not a function

上面程式碼表示,對於普通的物件,for...in迴圈可以遍歷鍵名,for...of迴圈會報錯。

一種解決方法是,使用Object.keys方法將物件的鍵名生成一個數組,然後遍歷這個陣列。

for (var key of Object.keys(someObject)) {
  console.log(key + ': ' + someObject[key]);
}

另一個方法是使用 Generator 函式將物件重新包裝一下。

function* entries(obj) {
  for (let key of Object.keys(obj)) {
    yield [key, obj[key]];
  }
}

for (let [key, value] of entries(obj)) {
  console.log(key, '->', value);
}
// a -> 1
// b -> 2
// c -> 3

與其他遍歷語法的比較

以陣列為例,JavaScript 提供多種遍歷語法。最原始的寫法就是for迴圈。

for (var index = 0; index < myArray.length; index++) {
  console.log(myArray[index]);
}

這種寫法比較麻煩,因此陣列提供內建的forEach方法。

myArray.forEach(function (value) {
  console.log(value);
});

這種寫法的問題在於,無法中途跳出forEach迴圈,break命令或return命令都不能奏效

for...in迴圈可以遍歷陣列的鍵名。

for (var index in myArray) {
  console.log(myArray[index]);
}

for...in迴圈有幾個缺點。

  • 陣列的鍵名是數字,但是for...in迴圈是以字串作為鍵名“0”、“1”、“2”等等。
  • for...in迴圈不僅遍歷數字鍵名,還會遍歷手動新增的其他鍵,甚至包括原型鏈上的鍵。
  • 某些情況下,for...in迴圈會以任意順序遍歷鍵名。

總之,for...in迴圈主要是為遍歷物件而設計的,不適用於遍歷陣列。

for...of迴圈相比上面幾種做法,有一些顯著的優點。

for (let value of myArray) {
  console.log(value);
}
  • 有著同for...in一樣的簡潔語法,但是沒有for...in那些缺點。
  • 不同於forEach方法,它可以與breakcontinuereturn配合使用。
  • 提供了遍歷所有資料結構的統一操作介面。

下面是一個使用 break 語句,跳出for...of迴圈的例子。

for (var n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}

上面的例子,會輸出斐波納契數列小於等於 1000 的項。如果當前項大於 1000,就會使用break語句跳出for...of迴圈。