迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
内部迭代器
forEach, map, reduce...
外部迭代器
必须显式地请求迭代下一个元素
增加了调用的复杂度,但也增强了灵活性,可以手动控制迭代地过程或顺序
1const Iterator = function (array) {2 const current = 034 const next = function () {5 current += 16 }78 const isDone = function () {9 return current >= array.length10 }1112 const getCurrentItem = function () {13 return array[current]14 }1516 return {17 next,18 isDone,19 getCurrentItem,20 }21}
两个数组的 compare
1const compare = function (iterator1, iterator2) {2 while (!iterator1.isDone() && !iterator2.isDone()) {3 if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) {4 throw new Error('iterator1 和 iterator2 不相等')5 }6 iterator1.next()7 iterator2.next()8 }910 console.log('iterator1 和 iterator2 相等')11}
简化成 ES6 常见的模拟:
1const makeIterator = function (array) {2 let index = 03 return {4 next() {5 return index < array.length6 ? { value: array[index++], done: false }7 : { value: undefined, done: true }8 }9 }10}
由上面 Iterator 代码可以看出一个生成器生成一个迭代器对象,迭代器的 next 方法进行迭代(流程控制),next 返回 value 和 done
ES6 迭代器
ES6 中的 Iterator 的作用有三个:
为各种数据结构,提供一个统一的、简便的访问接口
使得数据结构的成员能够按某种次序排列
ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。
ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被for...of循环遍历。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有(比如对象)。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
原生具备 Iterator 接口的数据结构如下:
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象
对象实现 for-of
1const iterableObj = {2 name: 'ahab',3 age: 18,4 key: 'value',5 [Symbol.iterator]() {6 let index = 07 const that = this // 缓存 this8 const keys = Object.keys(that)9 return {10 next() { // 也可以用箭头函数,但这种情况下 that 更常见11 return { value: that[keys[index++]], done: index > keys.length }12 }13 }14 },15}1617for (const value of iterableObj) { console.log(value) }
用 generator 优化:
1const iterableObj = {2 name: 'ahab',3 age: 18,4 key: 'value',5 * [Symbol.iterator]() {6 let index = 07 const keys = Object.keys(this)8 while (index < keys.length) yield this[keys[index++]]9 },10}1112for (const value of iterableObj) { console.log(value) }
其实对象本来就是无序的,完全没必要用 for-of,实现 for-of 遍历只是为了学习,更推荐用:
Object.keys()
Object.entries()
for-in(可能会遍历到原型链上,更推荐前两个)