ES6 迭代協議

迭代協議

可迭代協議(The iterable protocol) 和 迭代器協議(The iterator protocol)是對 ECMAScript 2015 的補充,不是新的內置或語法,僅僅是協議。能夠被任何遵循某些約定的對象來實現。函數

可迭代協議

可迭代協議容許 JavaScript 對象去定義或者定製它們的迭代行爲。this

一些內置類型是可迭代,例如 Array,TypeArray,Map,Set,String,在 for...of 結構中能夠循環遍歷值,而 Object 就不是。prototype

爲了變成可迭代對象,一個對象必須實現 @@iterator 方法, 意思是這個對象(或者它原型鏈 prototype chain 上的某個對象)必須有一個名字是 Symbol.iterator 的屬性:code

Property Value
[Symbol.iterator] 無參函數,該函數返回一個對象,該對象符合迭代器協議

迭代器協議

迭代器協議定義了一種標準的方式來產生有限或無限序列的值對象

當一個對象實現了 next() 方法,而且符合以下定義,則該對象就是一個迭代器對象。ip

屬性
next 一個無參函數,返回一個對象,該對象擁有兩個屬性 done 和 value

done(boolean)

  • 若是迭代器已經到了迭代序列的末尾,done 爲 false
  • 若是迭代器還能夠產生序列下一個值,done 爲 true

value

迭代器返回的任何 JavaScript 值,當 done 爲 true,可忽略。原型鏈

next 方法必須是返回包含 done 和 value 屬性的對象,若是非對象(類如 false, undefined)返回,將會拋出 TypeErrorget

例子

可迭代協議例子

自定義一個可迭代對象
const iterableObj = {}
iterableObj[Symbol.iterator] = function* () {
    yield 1
    yield 2
    yield 3
}
console.log([...iterableObj])
接受可迭代對象的內置 APIs

許多 APIs 接受可迭代對象做爲參數,例如 Map([iterable]),WeakMap([iterable]),Set([iterable]),WeakSet([iterable])generator

const myObj = {}
new Map([[1, 'a'], [2, 'b'], [3, 'c']]).get(2)               // "b"
new WeakMap([[{}, 'a'], [myObj, 'b'], [{}, 'c']]).get(myObj) // "b"
new Set([1, 2, 3]).has(3)                                    // true
new Set('123').has('2')                                      // true
new WeakSet(function* () {
    yield {}
    yield myObj
    yield {}
}()).has(myObj)                                              // true
用於可迭代對象的語法

一些語句或者表達式可用於可迭代對象,例如 for...of 循環,spread operator,yield*,destructuring assignment。原型

// for...of
for (let i of [1, 2, 4]) {
    console.log(i)
    // 1
    // 2
    // 4
}

// spread operator
console.log([...'abc']) // ["a", "b", "c"]

// yield*
function* gen() {
    yield* ['a', 'b', 'c']
}
console.log(gen().next()) // {value: "a", done: false}

// destructuring assignment
[a, b] = new Set(['a', 'b'])
console.log(a) // a

迭代器協議例子

簡單迭代器
function makeIterator(array) {
    let nextIndex = 0
    return {
        next() {
            return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { done: true }
        }
    }
}

const it = makeIterator(['a', 'b'])

console.log(it.next()) // {value: "a", done: false}
console.log(it.next()) // {value: "a", done: false}
console.log(it.next()) // {done: true}
生成器
function* makeSimpleGenerator(arr) {
    let nextIndex = 0
    while (nextIndex < array.length) {
        yield array[nextIndex++]
    }
}

const it = makeSimpleGenerator(['a', 'b'])

console.log(it.next()) // {value: "a", done: false}
console.log(it.next()) // {value: "a", done: false}
console.log(it.next()) // {value: undefined, done: true}
ES6 class
class SimpleIterator {
    constructor(data) {
        this.data = data
        this.index = 0
    }

    [Symbol.iterator]() {
        return {
            next: () => {
                return this.index < this.data.length ? { value: this.data[this.index++], done: false } : { done: true }
            }
        }
    }
}

const simple = new SimpleIterator([1, 3, 9])

for (let i of simple){
    console.log(i) // 1 3 9
}

再談生成器 generator

從上面的示例能夠看出,generator 是比較特殊的,generator 既是一個 生成器對象,又是一個 可迭代對象!

const generatorObj = function* () {
    yield 1
    yield 2
    yield 3
}()

console.log(typeof generatorObj.next) // function

console.log(typeof generatorObj[Symbol.iterator]) // function

console.log(generatorObj[Symbol.iterator]() === generatorObj) // true

文章如有紕漏,歡迎你們提問交流!

參考

  1. MDN: Iteration protocols
相關文章
相關標籤/搜索