Skip to content

AHABHGK

迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。

内部迭代器

forEach, map, reduce...

外部迭代器

必须显式地请求迭代下一个元素

增加了调用的复杂度,但也增强了灵活性,可以手动控制迭代地过程或顺序

1const Iterator = function (array) {
2 const current = 0
3
4 const next = function () {
5 current += 1
6 }
7
8 const isDone = function () {
9 return current >= array.length
10 }
11
12 const getCurrentItem = function () {
13 return array[current]
14 }
15
16 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 }
9
10 console.log('iterator1 和 iterator2 相等')
11}

简化成 ES6 常见的模拟:

1const makeIterator = function (array) {
2 let index = 0
3 return {
4 next() {
5 return index < array.length
6 ? { value: array[index++], done: false }
7 : { value: undefined, done: true }
8 }
9 }
10}

由上面 Iterator 代码可以看出一个生成器生成一个迭代器对象,迭代器的 next 方法进行迭代(流程控制),next 返回 value 和 done

ES6 迭代器

ES6 中的 Iterator 的作用有三个:

  1. 为各种数据结构,提供一个统一的、简便的访问接口

  2. 使得数据结构的成员能够按某种次序排列

  3. 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 = 0
7 const that = this // 缓存 this
8 const keys = Object.keys(that)
9 return {
10 next() { // 也可以用箭头函数,但这种情况下 that 更常见
11 return { value: that[keys[index++]], done: index > keys.length }
12 }
13 }
14 },
15}
16
17for (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 = 0
7 const keys = Object.keys(this)
8 while (index < keys.length) yield this[keys[index++]]
9 },
10}
11
12for (const value of iterableObj) { console.log(value) }

其实对象本来就是无序的,完全没必要用 for-of,实现 for-of 遍历只是为了学习,更推荐用:

  • Object.keys()

  • Object.entries()

  • for-in(可能会遍历到原型链上,更推荐前两个)

流程控制

Generator 函数的异步应用

async 函数