1. 程式人生 > >ES6迭代協議(iteration protocol)

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

. Some bulit-in types are built-in iterable objects and have default iteration behavior, such as Array or Map, but other types (e.g. Object) not.

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, 返回自身