深刻理解JS迭代協議——手寫迭代器

可迭代協議和迭代器協議

參考MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols數據結構

可迭代協議

可迭代協議容許 JavaScript 對象去定義或定製它們的迭代行爲, 例如(定義)在一個 for..of 結構中什麼值能夠被循環(獲得)。一些內置類型都是內置的可迭代類型而且有默認的迭代行爲, 好比 Array or Map, 另外一些類型則不是 (好比Object) 。
爲了變成可迭代對象, 一個對象必須實現 @@iterator 方法, 意思是這個對象(或者它原型鏈 prototype chain 上的某個對象)必須有一個名字是 Symbol.iterator 的屬性。
當一個對象須要被迭代的時候(好比開始用於一個for..of循環中),它的@@iterator方法被調用而且無參數,而後返回一個用於在迭代中得到值的迭代器函數

迭代器協議

該迭代器協議定義了一種標準的方式來產生一個有限或無限序列的值,而且當全部的值都已經被迭代後,就會有一個默認的返回值。
當一個對象只有知足下述條件纔會被認爲是一個迭代器:
它實現了一個 next() 的方法而且擁有如下含義
返回一個對象的無參函數,被返回對象擁有兩個屬性:this

  • done(boolean)prototype

    • true:迭代器已經超過了可迭代次數。這種狀況下,value的值能夠被省略
    • 若是迭代器能夠產生序列中的下一個值,則爲 false。這等效於沒有指定done這個屬性。
  • value- 迭代器返回的任何 JavaScript 值。done 爲 true 時可省略。

next 方法必需要返回一一個對象,該對象有兩個必要的屬性: done和value,若是返回一個非對象值(好比false和undefined) 會展現一個 TypeError("iterator.next() returned a non-object value") 的錯誤code

var myIterator = {
    next: function() {
        // ...
    },
    [Symbol.iterator]: function() { return this }
}

可迭代對象(Iterable)

  • 知足可迭代協議的對象是可迭代對象。
  • 可迭代協議: 對象的[Symbol.iterator]值是一個無參函數,該函數返回一個迭代器(Iterator)。
  • 原生具有 Iterator 接口的數據結構以下。對象

    • Array
    • Map
    • Set
    • String
    • TypedArray
    • 函數的 arguments 對象
    • NodeList 對象
// for...of會獲取可迭代對象的[Symbol.iterator](),對該迭代器逐次調用next()
   // 直到迭代器返回對象的done屬性爲true時,遍歷結束
    for (let value of ["a", "b", "c"]) {
      console.log(value);
    }

手寫一個迭代器(Iterator)

/* 
        這是一個手寫的迭代器(Iterator)
        知足迭代器協議的對象。
        迭代器協議: 對象的next方法是一個無參函數,它返回一個對象,該對象擁有done和value兩個屬性: 
    */
    var it = makeIterator(["a", "b"]);

    it.next(); // { value: "a", done: false }
    it.next(); // { value: "b", done: false }
    it.next(); // { value: undefined, done: true }

    function makeIterator(array) {
      var nextIndex = 0;
      return {
        next: function() {
          return nextIndex < array.length
            ? { value: array[nextIndex++], done: false }
            : { value: undefined, done: true };
        },
      };
    }

使本身迭代器可迭代(迭代器返回可迭代對象(Iterable))

一個良好的迭代即實現了迭代器協議,又實現了可迭代協議,方式就是可迭代協議返回的是自身接口

/* 
        使迭代器可迭代
        makeIterator函數生成的迭代器並無實現可迭代協議
        因此不能在for...of等語法中使用。
        能夠爲該對象實現可迭代協議,在[Symbol.iterator]函數中返回該迭代器自身
        重新名了下函數名稱createIterator
    */
    function createIterator(array) {
      var nextIndex = 0;
      return {
        next: function() {
          return nextIndex < array.length
            ? { value: array[nextIndex++], done: false }
            : { value: undefined, done: true };
        },
        [Symbol.iterator]: function () { 
            console.log("返回的迭代器:",this)
            return this // 注意這裏是對象調用模式,this指向的就是上層的對象,迭代器
        }
      };
    }
    
    var iterator = createIterator([1, 2, 3]);
    console.log(...iterator)

什麼是生成器(Generator)

上面的函數看出,手動寫個iterator太麻煩了,因此ES6推出generator,方便建立iterator。也就是說,generator就是一個返回值爲迭代器iterator的函數。(感受就是咱們上面函數的語法糖)
生成器對象既是迭代器,又是可迭代對象ip

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

var aGeneratorObject = aGeneratorfunction()

// 知足迭代器協議,是迭代器
aGeneratorObject.next()   // {value: 1, done: false}
aGeneratorObject.next()   // {value: 2, done: false}
aGeneratorObject.next()   // {value: 3, done: false}
aGeneratorObject.next()   // {value: undefined, done: true}

// [Symbol.iterator]是一個無參函數,該函數執行後返回生成器對象自己(是迭代器),因此是可迭代對象
aGeneratorObject[Symbol.iterator]() === aGeneratorObject   // true

// 能夠被迭代
var aGeneratorObject1 = aGeneratorfunction()
[...aGeneratorObject1]   // [1, 2, 3]

在生成器中return

遍歷返回對象的done值爲true時迭代即結束,不對該value處理(return會把done置爲done)原型鏈

function *createIterator() {
  yield 1;
  return 42;
  yield 2;
}

let iterator = createIterator();
iterator.next();   // {value: 1, done: false}
iterator.next();   // {value: 42, done: true}
iterator.next();   // {value: undefined, done: true}
let iterator1 = createIterator();
console.log(...iterator);   // 1

生成器委託yield*

function* g1() {
  yield 1;
  yield 2;
}

function* g2() {
  yield* g1();
  yield* [3, 4];
  yield* "56";
  yield* arguments;
}

var generator = g2(7, 8);
console.log(...generator);   // 1 2 3 4 "5" "6" 7 8
相關文章
相關標籤/搜索