Fork me on GitHub

前端设计模式之【迭代器模式】

设计模式之迭代器模式!

一、迭代器模式

迭代器模式主要的目的就是——遍历。

我们一般通过 forEach 来遍历数组的数据,但是对于一些情况没有该遍历方法,比如在 NodeList 中,遍历所有的节点,并不能遍历出来。因为 NodeList 是一个类数组的对象,并没有提供任何的遍历方法。

那么问题来了,如果想让想让这种类数组的像数组一样提供遍历。那么迭代器就是解决的这种问题,我们不用关心数据集合的内部实现结构。都能通过一个迭代器遍历出来,这就是迭代器模式。

二、jQuery 中的迭代器模式

jQuery 中的遍历器 each 方法就是一个很好的例子,它的设计就是封装了一套规则,可以遍历不同的集合对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const arr = [1, 2, 3]
const aNodes = document.getElementsByTagName('a')

$.each(arr, function (index, item) {
console.log(`数组的第${index}个元素是${item}`)
})

$.each(aNodes, function (index, aNode) {
console.log(`DOM类数组的第${index}个元素是${aNode.innerText}`)
})

const jQNodes = $('a')
$.each(jQNodes, function (index, aNode) {
console.log(`jQuery集合的第${index}个元素是${aNode.innerText}`)
})

三、ES6 中的迭代器

我们知道迭代器模式的作用,到了 ES6 中,又扩展了很多的集合对象,Set 、Map 等,他们都有自己内部的实现方式,我们还是想要新的一套规则去设计它。ES6 在推出新数据结构的同时也推出了一套统一的接口机制——迭代器(Iterator

ES6 规定,每个迭代器对象都要有一个 Symbol.iterator 属性,该属性对应的方法本质上是当前数据结构默认的迭代器生成函数,可以被遍历,被 for...of... 循环和迭代器的 next 方法遍历。 事实上,for...of... 的背后正是对next方法的反复调用。

3.1 iterator 的内部调用:
1
2
3
4
5
6
7
8
const arr = [1, 2, 3]
// 通过调用iterator,拿到迭代器对象
const iterator = arr[Symbol.iterator]()

// 对迭代器对象执行next,就能逐个访问集合的成员
iterator.next()
iterator.next()
iterator.next()
3.2 for—-of— 借助 iterator 实现

for…of… 其实就是 iterator 循环调用换了种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 通过调用iterator,拿到迭代器对象
const iterator = arr[Symbol.iterator]()

// 初始化一个迭代结果
let now = { done: false }

// 循环往外迭代成员
while(!now.done) {
now = iterator.next()
if(!now.done) {
console.log(`现在遍历到了${now.value}`)
}
}
3.3 生成器函数
1
2
3
4
5
6
7
8
9
10
11
12
// 编写一个迭代器生成函数
function *iteratorGenerator() {
yield '1号选手'
yield '2号选手'
yield '3号选手'
}

const iterator = iteratorGenerator()

iterator.next()
iterator.next()
iterator.next()
3.4 用 ES5 实现一个生成器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 定义生成器函数,入参是任意集合
function iteratorGenerator(list) {
// idx记录当前访问的索引
var idx = 0
// len记录传入集合的长度
var len = list.length

// 返回一个迭代器对象,迭代器对象可以调用 next 方法进行执行(闭包)
return {
// 自定义next方法
next: function() {
// 如果索引还没有超出集合长度,done为false
var done = idx >= len
// 如果done为false,则可以继续取值
var value = !done ? list[idx++] : undefined

// 将当前值与遍历是否完毕(done)返回
return {
done: done,
value: value
}
}
}
}

var iterator = iteratorGenerator(['1号选手', '2号选手', '3号选手'])
iterator.next()
iterator.next()
iterator.next()