ES6迭代協議(iteration protocol)
A couple of additions to ES2015 were not built-in or syntax, but protocols. These protocols can be implemented by any object respecting some conventions.
There are two protocols: iterable protocol
and iterator protocol
.
Iterable protocol
Iterable protocol allows Javascript objects to define or customize the their behavior. For example, what values can be looped over in for ... of
In order to be iterable object, an object have to implement @@iterator
method, meaning that it (or other objects up prototype chain) must have a property named Symbol.iterator
- Built-in iterable objects
// String, Array, TypedArray, Map, Set
console.log(...'this')
// 't', 'h', 'i', 's'
- Customizid iterable objects
let o = {}
o[Symbol.iterator] = function* () {
yield 1
yield 2
yield 3
}
[...o] // [1, 2, 3]
- API accepting iterable objects
// Set([iterable]), Map([itetable]), Array.from([itetable])
// Promise.all([iterable]), Promise.race([iterable])
Array.from('this') // ['t', 'h', 'i', 's']
new Set([1, 2, 2]) // Set {1, 2}
- Syntax using iterable objects
// for-of, spread operator(...), yield*, destructing assignment
let [a, b, c, d] = 'test'
function* gen() { yield* [1, 2, 3] }
console.log(...gen()) // 1, 2, 3
- Non-well-formed iterable objects
let o = {}
o[Symbol.iterator] = () => 1
...o
// TypeError: Result of the Symbol.iterator is not an object
Iterator protocol
The iterator
protocol defines a standard way in which a finate or infinate sequence value can be producted.
An object is considered as a iterator when it implements a next()
method with the following semantics.
function makeIter(len) {
let val = 0
return val < len ? {value: val++, done: false} : {done: true}
// 無限迭代
// return {value: val++, done: false}
}
let iter = makeIter(2)
iter.next() // {value: 0, done: false}
iter.next() // {value: 1, done: false}
iter.next() // {done: true}
let it = makeIter(3)
let o = {}
// Symbol.iterator是一個返回iterator的function
o[Symbol.iterator] = () => it
it.next() // 注意執行了一次next()
console.log([...o]) // [2, 3]
console.log([...o]) // [], it已經迭代完畢
function makeIter(len) {
let val = 0
// 相當於{value: undefined, done: undefined/false},{done: true}
return val < len ? {v: val++} : {done: 1}
// 如果不是物件,執行[...o]時報錯,TypeError
// return 1
}
let it = makeIter(3)
let o = {}
o[Symbol.iterator] = () => it
[...o]
// [undefined, undefined, undefined]
Is a generator object an iterator or an iterable?
function* gen() {yield* [1, 2, 3]}
let g = gen()
// generator物件既是iterator又是iterable
g.next // [function: next]
g[Symbol.iterator] // [function: [Symbol.iterator]]
g[Symbol.iterator]() === g // true, 返回自身