迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而不需要暴露该对象的内部表示。
迭代器分为内部迭代器和外部迭代器。内部迭代器只需一次初始调用,而外部迭代器必须显式地请求迭代下一个元素,这样我们就可以手动控制迭代过程。
实现一个内部迭代器:
Array.prototype.innerIterator = function(callback){for (let i = 0, len = this.length; 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
实现一个外部迭代器:
Array.prototype.outerInterator &#61; function(){let index &#61; 0;return {next: () &#61;> {return index <this.length ?{value: this[index&#43;&#43;], done: false}:{value: undefined, done: true}}}
}
let iterator &#61; [1,2,3].outerInterator();for(let next; (next &#61; iterator.next()) && !next.done;) {console.log(&#39;item&#39;, next.value)
}
// item 1
// item 2
// item 3
迭代协议
了解了迭代器模式&#xff0c;再来看看 ES6 中补充的迭代协议。可迭代&#xff08;iterable&#xff09;协议和迭代器&#xff08;iterator&#xff09;协议。
可迭代协议&#xff1a;
一个可迭代对象&#xff08;或其原型上&#xff09;&#xff0c;必须有一个 Symbol.iterator
的属性&#xff0c;该属性所对应的值为返回一个对象的无參函数&#xff0c;被返回对象符合迭代器协议。当可迭代对象需要迭代时&#xff0c;调用该方法。
一些数据类型内置了 &#64;&#64;iterator
方法&#xff0c;有自己默认的迭代行为。&#xff08;String, Array, TypedArray, Map , Set 等都是内置可迭代对象&#xff0c; 因为它们的原型对象都有一个 &#64;&#64;iterator
方法.&#xff09;&#xff08;[Symbol.iterator]
、&#64;&#64;iterator
可以认为是一回事&#xff09;
let iterator &#61; (&#39;hi&#39;)[Symbol.iterator]()
var a &#61; iterator.next();
// a { value: &#39;h&#39;, done: false }
迭代器协议&#xff1a;
一个迭代器必须实现了 next()
方法&#xff0c;该方法是返回一个对象的无參函数。被返回的对象有两个必要的属性&#xff1a;done 和 value。
Array.prototype.Iteration &#61; function(){let index &#61; 0;return {[Symbol.iterator](){return this},next: () &#61;> {return index <this.length ?{value: this[index&#43;&#43;], done: false}:{value: undefined, done: true}}}
};
let Iteration &#61; [2, 3, 4].Iteration();
for(let value of Iteration) {console.log(&#39;value&#39;, value)
}
// value 2
// value 3
// value 4
不能发现&#xff0c;Iteration 同时满足可迭代协议和迭代协议。又因为是可迭代的&#xff0c;for...of
是可以直接使用&#xff0c;而且这个和外部迭代器十分相似。
一旦一种数据结构有了 &#64;&#64;iterator
方法后&#xff0c; 就认为是可迭代的。ES6 中许多新的方法就是基于此的 解构赋值
、扩展运算符
、yield*
&#xff0c;还有 for..of
、Array.from()
等。
知道了以上知识&#xff0c;也就知道了为什么对象不可以直接使用 for...of
了。不过我们可以在对象原型上添加 &#64;&#64;iterator
方法&#xff0c;使之成为可迭代的。
Object.prototype.Iteration &#61; function(){let keys &#61; Object.keys(this), index &#61; 0;return{[Symbol.iterator](){return this},next: () &#61;> {let current &#61; index&#43;&#43;;return current
}
let iterator &#61; {&#39;a&#39;: 1, &#39;b&#39;: 2, &#39;c&#39;: 3}.Iteration();for(let [key, value] of iterator) {console.log(&#39;key:&#39;, key, &#39;value:&#39;, value)
}
// key: a value: 1
// key: b value: 2
// key: c value: 3
生成器
像以上的的对象都是我们自己手动实现的&#xff0c;符合可迭代协议和迭代协议的对象。看起来很麻烦&#xff0c;还好这些工作已经有函数替我们做了&#xff0c;那就是生成器函数。
生成器函数是可以作为迭代器工厂的函数&#xff0c;当它被执行时会返回一个新的 Generator 对象&#xff0c;该对象符合可迭代协议和迭代器协议。
现在我们用生成器函数使得对象符合迭代协议&#xff1a;
Object.prototype.Iteration &#61; function *(){for(let [key, value] of Object.entries(this)){yield [key, value]}
}
for(let [key, value] of {&#39;a&#39;: 1, &#39;b&#39;: 2, &#39;c&#39;: 3}.Iteration()) {console.log(&#39;key:&#39;, key, &#39;value:&#39;, value)
}
// key: a value: 1
// key: b value: 2
// key: c value: 3
在这里生成器函数只是作为生产迭代器的工厂而已&#xff0c;其实它还是消息双向传递系统。也正是这些特性的存在&#xff0c;使得异步流程控制又向前迈了一大步。