迭代器模式是指提供一種方法順序訪問一個聚合對象中的各個元素,而不須要暴露該對象的內部表示。數據結構
迭代器分爲內部迭代器和外部迭代器。內部迭代器只需一次初始調用,而外部迭代器必須顯式地請求迭代下一個元素,這樣咱們就能夠手動控制迭代過程。異步
實現一個內部迭代器:函數
Array.prototype.innerIterator = function(callback){ for (let i = 0, len = this.length; i < len; i++) { callback && callback.call(this[i], this[i], i) } }; [1,2,3].innerIterator(function(item, index){ console.log('item:', item, 'index:', index) }) // item: 1 index: 0 // item: 2 index: 1 // item: 3 index: 2
實現一個外部迭代器:this
Array.prototype.outerInterator = function(){ let index = 0; return { next: () => { return index < this.length ? {value: this[index++], done: false}: {value: undefined, done: true} } } } let iterator = [1,2,3].outerInterator(); for(let next; (next = iterator.next()) && !next.done;) { console.log('item', next.value) } // item 1 // item 2 // item 3
瞭解了迭代器模式,再來看看 ES6 中補充的迭代協議。可迭代(iterable)協議和迭代器(iterator)協議。prototype
可迭代協議:
一個可迭代對象(或其原型上),必須有一個 Symbol.iterator
的屬性,該屬性所對應的值爲返回一個對象的無參函數,被返回對象符合迭代器協議。當可迭代對象須要迭代時,調用該方法。code
一些數據類型內置了 @@iterator
方法,有本身默認的迭代行爲。(String, Array, TypedArray, Map , Set 等都是內置可迭代對象, 由於它們的原型對象都有一個 @@iterator
方法.)([Symbol.iterator]
、@@iterator
能夠認爲是一回事)對象
let iterator = ('hi')[Symbol.iterator]() var a = iterator.next(); // a { value: 'h', done: false }
迭代器協議:
一個迭代器必須實現了 next()
方法,該方法是返回一個對象的無參函數。被返回的對象有兩個必要的屬性:done 和 value。ip
Array.prototype.Iteration = function(){ let index = 0; return { [Symbol.iterator](){return this}, next: () => { return index < this.length ? {value: this[index++], done: false}: {value: undefined, done: true} } } }; let Iteration = [2, 3, 4].Iteration(); for(let value of Iteration) { console.log('value', value) } // value 2 // value 3 // value 4
不能發現,Iteration 同時知足可迭代協議和迭代協議。又由於是可迭代的,for...of
是能夠直接使用,並且這個和外部迭代器十分類似。原型
一旦一種數據結構有了 @@iterator
方法後, 就認爲是可迭代的。ES6 中許多新的方法就是基於此的 解構賦值
、擴展運算符
、yield*
,還有 for..of
、Array.from()
等。it
知道了以上知識,也就知道了爲何對象不能夠直接使用 for...of
了。不過咱們能夠在對象原型上添加 @@iterator
方法,使之成爲可迭代的。
Object.prototype.Iteration = function(){ let keys = Object.keys(this), index = 0; return{ [Symbol.iterator](){return this}, next: () => { let current = index++; return current < keys.length? {value: [keys[current], this[keys[current]]], done: false}: {value: undefined, done: true}; } } } let iterator = {'a': 1, 'b': 2, 'c': 3}.Iteration(); for(let [key, value] of iterator) { console.log('key:', key, 'value:', value) } // key: a value: 1 // key: b value: 2 // key: c value: 3
像以上的的對象都是咱們本身手動實現的,符合可迭代協議和迭代協議的對象。看起來很麻煩,還好這些工做已經有函數替咱們作了,那就是生成器函數。
生成器函數是能夠做爲迭代器工廠的函數,當它被執行時會返回一個新的 Generator 對象,該對象符合可迭代協議和迭代器協議。
如今咱們用生成器函數使得對象符合迭代協議:
Object.prototype.Iteration = function *(){ for(let [key, value] of Object.entries(this)){ yield [key, value] } } for(let [key, value] of {'a': 1, 'b': 2, 'c': 3}.Iteration()) { console.log('key:', key, 'value:', value) } // key: a value: 1 // key: b value: 2 // key: c value: 3
在這裏生成器只是做爲迭代器而已,其實它仍是消息雙向傳遞系統。也正是這些特性的存在,使得異步流程控制又向前邁了一大步。