TypeScript迭代器

不會爬樹的猴發表於2019-02-16

迭代器

重寫迭代器方法需 ECMAScript 2015(ES6) , TypeScript 需 1.5 以上版本。

可迭代協議

可迭代協議 允許 JavaScript 物件去定義或定製它們的迭代行為, 例如(定義)在一個 for..of 結構中什麼值可以被迴圈(得到)。一些內建型別都是內建的可遍歷物件並且有預設的迭代行為, 比如 Array or Map, 另一些型別則不是 (比如Object) 。

為了變成可遍歷物件, 一個物件必須實現 @@iterator 方法, 意思是這個物件(或者它原型鏈prototype chain上的某個物件)必須有一個名字是 Symbol.iterator 的屬性:

使用迭代器

以下程式碼全部使用 TypeScript, TypeScript 版本號為 2.0.10, 使用 ES6 規範。

var someArray = [1, 5, 7];
var someArrayEntries = someArray.entries();         
console.log(someArrayEntries.toString());           // "[object Array Iterator]"
console.log(someArrayEntries === someArrayEntries[Symbol.iterator]());    // true
console.log([...someArray] );                         //[1,5,7]
let iterator = someArrayEntries[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

//[Symbol.iterator]    返回一個物件的無參函式,被返回物件需符合迭代器協議。

重寫迭代器

someArray[Symbol.iterator] = ()=>{
  return {
    _index:0,
    next: function() {
        if(this._index < someArray.length ){
            return { value: someArray[this._index++], done: false}
        }else{
            return { value: undefined, done:true}
        }
    },
    [Symbol.iterator]:()=>{
        return this;
    }
  };
};
//在js檔案中,可以省略掉對 [Symbol.iterator] 屬性的定義。
//next() 返回的資料中,
//done為布林值,按照約定應為是否已經進行到函式邊界的描述
//value 則為當前可迭代物件的某個屬性值。在js檔案中, done為true 可省略。但實際上,value可以為任何js物件

let someArrayIterator = someArray[Symbol.iterator]();
console.log(someArrayIterator.next()); //{ value: 1, done: false }
console.log(someArrayIterator.next()); //{ value: 5, done: false }
console.log(someArrayIterator.next()); //{ value: 7, done: false }
console.log(someArrayIterator.next()); //{ value: undefined, done: true }
console.log(someArrayIterator.next()); //{ value: undefined, done: true }


someArrayEntries[Symbol.iterator] = ()=>{
  return {
    _index:0,
    next: function() {
        if(this._index < someArray.length){
            return { value: [this._index,someArray[this._index++]], done: false}
        }else{
            return { value: [this._index,undefined], done:true}
        }
    },
    [Symbol.iterator]:()=>{
        return this;
    }
  };
};


console.log(someArrayEntries === someArrayEntries[Symbol.iterator]()); //false
iterator = someArrayEntries[Symbol.iterator](); 
console.log(iterator.next());                   //{ value: [ 0, 1 ], done: false }
console.log(iterator.next());                   //{ value: [ 1, 5 ], done: false }
console.log(iterator.next());                   //{ value: [ 2, 7 ], done: false }
console.log(iterator.next());                   //{ value: [ 3, undefined ], done: true }
console.log(iterator.next());                   //{ value: [ 3, undefined ], done: true }

內建迭代器行為

for(let obj of someArray){
    console.log(obj);
}
// 1 5 7

let someArrayIterator = someArray[Symbol.iterator]();
let obj = someArrayIterator.next();
while(!obj.done){
    console.log(obj.value)
    obj = someArrayIterator.next();
}
// 1 5 7

//這裡使用 while 來 模擬一部分 for...of 的邏輯。


console.log([...someArray] );    // [ 1, 5, 7 ]
console.log([...someArrayEntries]);//[ [ 0, 1 ], [ 1, 5 ], [ 2, 7 ] ]

//內建可迭代物件皆可使用上述2種語法進行遍歷其鍵值對。
//String, Array, TypedArray, Map and Set 是內建可迭代物件, 因為它們的原型物件都有一個 @@iterator 方法。
//除此之外由使用者自己實現的 @@iterator 方法也可以使用上述2中語法。

生成器函式

function* 宣告 (function關鍵字後跟一個星號)定義了一個生成器函式 (generator function),它返回一個 Generator 物件。

注意:箭頭函式 無法作為生成器函式。

  1. 生成器

    >生成器是一種可以從中退出並在之後重新進入的函式。生成器的環境(繫結的變數)會在每次執行後被儲存,下次進入時可繼續使用。
  2. yield

    >yield 關鍵字用來暫停和恢復一個生成器函式 ( (function* 或 legacy generator).
  3. yield*

    >yield* 可以把需要 yield 的值委託給另外一個生成器或者其他任意的可迭代物件。
    
function* anotherGenerator(i) {
  yield i + 1;  //yield 會返回當前語句的值,類似於return。但在生成器函式中,return受到使用限制。
  yield i + 2;
  yield i + 3;
}

function* generator(i){
  yield i;
  yield* anotherGenerator(i);
  yield i + 10;
}

var gen = generator(10);
//執行生成器函式會返回一個生成器,儲存了當前函式執行環境上下文,同時定義了next()方法來恢復函式執行直至下一條yield語句。

console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
//使用生成器函式來實現迭代功能,很顯然比之前實現 Iterator 系列介面要方便快捷的多。

相關文章