for in與for of及遍歷器Iterator

for in

for in可以用來遍歷對象的可枚舉屬性列表(包括[[prototype]]鏈),單獨使用in操作符會檢查屬性是否在對象及其[[prototype]]原型鏈中(無論是否可以枚舉)

var obj = {
  a: 2
}
console.log("a" in obj);//true

但是使用for in循環輸出的屬性名的順序是不可預測的,返回的先后次序因瀏覽器而異。

使用for in也可以遍歷數組,屬性為數組的索引,但是會有以下問題:

  • index索引為字符串型數字,不能直接進行幾何運算
  • 遍歷順序有可能不是按照實際數組的內部順序
  • 使用for in會遍歷數組所有的可枚舉屬性,包括原型。例如例子中的原型方法method和name屬性
Alt text

因此for in更適合用來遍歷對象

for of

前面說了for in遍歷的是key(鍵名),使用ES6的for of可以遍歷數組的value(鍵值),mdn上對于for of的說明

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

var arr = [ 1, 2, 3];
for (var v of arr) {
  console.log( v );
}
// 1
// 2
// 3

for of循環不僅支持數組,實際上述定義的可迭代對象,可迭代對象指的是具備Iterator接口的數據結構

Iterator

實際上for of遍歷就是基于Iterator,當使用for of循環遍歷某種數據結構時,該循環會自動去尋找Iterator接口

遍歷器(Iterator)是一種接口,為各種不同的數據結構提供統一的訪問機制。任何數據結構只要部署Iterator接口,就可以完成遍歷操作(即依次處理該數據結構的所有成員)。

Iterator 的作用有三個:一是為各種數據結構,提供一個統一的、簡便的訪問接口;二是使得數據結構的成員能夠按某種次序排列;三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。

ES6 規定,默認的 Iterator 接口部署在數據結構的Symbol.iterator屬性,或者說,一個數據結構只要具有Symbol.iterator屬性,就可以認為是“可遍歷的”(iterable)。Symbol.iterator屬性本身是一個函數,就是當前數據結構默認的遍歷器生成函數。執行這個函數,就會返回一個遍歷器。至于屬性名Symbol.iterator,它是一個表達式,返回Symbol對象的iterator屬性

原生具備Iterator接口的數據結構如下

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函數的arguments對象
  • NodeList對象

for of循環首先會向北訪問對象請求一個迭代器對象,然后通過調用迭代器對象的next()方法來遍歷所有返回值

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

上面代碼中,變量arr是一個數組,原生就具有遍歷器接口,部署在arr的Symbol.iterator屬性上面。所以,調用這個屬性,就得到遍歷器對象。value是當前的遍歷值,done是一個布爾值,表示是否還有可以遍歷的值,這里遍歷到第三個時,已經沒有可以遍歷的值了,但是done仍然是false,下一個才是true,這個機制和ES6中發生器函數的語義相關,這里不做討論。

普通的對象沒有內置的Iterator接口,無法用for of遍歷,但是我們可以用Symbol.iterator來自己完成對對象的遍歷

var myObject = {
  a: 2,
  b: 3,
  c: 4
};

Object.defineProperty( myObject, Symbol.iterator, {
  enumerable: false,
  writeable: false,
  configurable: true,
  value: function() {
    var o = this;
    var idx = 0;
    var ks = Object.keys( o );
    return {
      next: function() {
        return {
          value: o[ks[idx++]],
          done: (idx > ks.length)
        };
      }
    };
  }
});

var it = myObject[Symbol.iterator]();
it.next(); //{ value: 2, done:false}
it.next(); //{ value: 3, done:false}
it.next(); //{ value: 4, done:false}
it.next(); //{ value: undefined, done:true}

for (var v of myObject) {
  console.log( v );
}
// 2
// 3
// 4

原文地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容