What makes JavaScript Iterables Iterable? - Iterator Protocol and Generator
什麼讓 JavaScript 資料結構能夠被迭代? 了解 Iterator Protocol 與 Generator
前言
你有想過為什麼陣列可以被迭代處理(for...of
)但物件不行嗎?JavaScript Iterables (可迭代物件)背後仰賴 Iterator Protocol 來實踐可被迭代的資料結構。
只要一個資料結構實作了這個協議,就能成為 Iterable,被 for...of
、展開運算子、Array.from()
或解構賦值等……語法直接操作。
基於 Iterator Protocol 的概念更是伸展到 Generator Function 相關的知識點,可以創造更多特殊的資料結構。
Iterables
所謂「可迭代」,指的是物件實作了內建的特殊屬性 Symbol.iterator
,並回傳一個「迭代器」。
舉例來說,陣列天生就是可迭代的:
const arr = [1, 2, 3];console.log(typeof arr[Symbol.iterator]); // 'function'
Iterator
Iterator 必須透過 next
來進入下一個迭代,每次呼叫 next()
都會回傳一個物件 { value, done }
,分別代表目前的值與是否結束:
const iterator = arr[Symbol.iterator]();console.log(iterator.next()); // { value: 1, done: false }console.log(iterator.next()); // { value: 2, done: false }console.log(iterator.next()); // { value: 3, done: false }console.log(iterator.next()); // { value: undefined, done: true }
範例
可以自己定義一個可迭代的物件,例如一個「範圍生成器」:
const range = { from: 1, to: 3, [Symbol.iterator]() { let current = this.from; const end = this.to; return { next() { if (current <= end) { return { value: current++, done: false }; } else { return { done: true }; } }, }; },};
for (const num of range) { console.log(num); // 1, 2, 3}
Generator:更簡潔的 Iterator 實作方式
上面的 range
雖然可行,但看起來有點繁瑣。
Generator 函式提供了一種更直覺的方式來建立迭代器。
function* range(from, to) { for (let i = from; i <= to; i++) { yield i; }}
for (const num of range(1, 3)) { console.log(num); // 1, 2, 3}
Generator 是一種特殊的函式它並不會立即執行它內部的程式碼,可以在執行過程中「暫停」與「繼續」,每次執行 yield
都會回傳一個 { value, done }
結構的結果,改寫先前案例:
function* threeStepGenerator() { yield 1; yield 2; yield 3; console.log('End');}
const gen = threeStepGenerator();console.log('Generator 已建立');
console.log(gen.next()); // { value: 1, done: false }console.log(gen.next()); // { value: 2, done: false }console.log(gen.next()); // { value: 3, done: false }console.log(gen.next()); // { value: undefined, done: true }
總結
- Iterable:擁有
Symbol.iterator
方法的物件。 - Symbol.iterator:具有
next()
方法、能逐步回傳{ value, done }
的物件。 - Generator:透過
function*
定義、內部使用yield
產生值的語法糖,本質上自動幫你建立 Iterator。
實戰上惰性求值、非同步控制、資料流生成、協程模擬、迭代器封裝都可以應用上相關概念。
延伸閱讀
- JavaScript Iterators Explained with Examples | Level up Your JS - Ijemma Onwuzulike
- JavaScript Generators in 7 Minutes: What They Are and How They’re the Overpowered Iterator - Ijemma Onwuzulike
- Iterables - javascript.info
- Generators - javascript.info
- [Day 26] Lazy Evaluation 和 Generator Function - Monica