迭代器模式:提供一种方法顺序一个聚合对象中各个元素,而又不暴露该对象内部表示。迭代器模式的特点在于顺序访问一个集合,使用者无需知道内部结构。
在现实生活中好像例子不多,但是熟悉ES6的工程师应该知道es6中有iterator。我们先不看这个,先写一个小的demo代码来熟悉迭代器。
我们先看一下迭代器的UML类图
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
30
31 1class Iterator {
2 constructor(container) {
3 this.list = container.list;
4 this.index = 0;
5 }
6 next() {
7 if (this.hasNext()) {
8 return this.list[this.index++];
9 } else this.index++;
10 return null;
11 }
12 hasNext() {
13 return this.index < this.list.length;
14 }
15}
16class Container {
17 constructor(list) {
18 this.list = list;
19 }
20 getIterator() {
21 return new Iterator(this);
22 }
23}
24
25const arr = [1, 2, 3, 4, 6];
26
27const iter = new Container(arr).getIterator();
28while (iter.hasNext()) {
29 console.log(iter.next());
30}
31
我们获取到迭代器对象后,无需关注迭代器是如何遍历子元素的,只需要简单的调用next函数即可。
ES6的iteractor你可能没有用过,但是for of循环你肯定使用过。
在JS当中,有很多有顺序的聚合对象比如array,map, set, nodeList, arguments,类数组,这些数据类型上面有一个Symbol.iterator属性,该值是一个函数用于返回一个迭代器,该迭代器上面有一个next函数。
1
2
3
4 1const arr = [1, 2, 3, 4, 6];
2const ite = arr[Symbol.iterator]();
3console.log(ite);
4
我们来使用ES6的iterator来遍历该数组。
1
2
3
4
5
6
7
8
9
10
11
12 1const arr = [1, 2, 3, 4, 6];
2let iterator = arr[Symbol.iterator]();
3
4let item = iterator.next();
5while (!item.done) {
6 console.log(item);
7 item = iterator.next();
8}
9
10console.log(iterator.next());
11console.log(iterator.next());
12
value:表示当前遍历的值 done表示是否结束 true表示结束
我们来封装一个each方法,来实现遍历。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 1const arr = [1, 2, 3, 4, 6];
2const set = new Set();
3set.add(1);
4set.add(2);
5set.add(3);
6set.add(4);
7
8
9function each(data) {
10 let iterator = data[Symbol.iterator]();
11 let item = iterator.next();
12 while (!item.done) {
13 console.log(item.value);
14 item = iterator.next();
15 }
16}
17
18console.log("数组遍历");
19each(arr);
20console.log("set遍历");
21each(set);
22
这样其实很麻烦,幸好ES6封装了for of循环 这个我们就不掩饰了,非常简单。
Generator函数
1
2
3
4
5
6
7
8 1function* list() {
2 yield 1;
3 yield 2;
4 yield 3;
5}
6const l = list();
7each(l);
8
Generator函数返回的是可以迭代的对象,我们可以使用each(上面封装的方法)或 for of来遍历。
我们知道Object没有迭代器,不能使用for of循环,那么我们可以通过Generator来实现for of遍历Obejct
1
2
3
4
5
6
7
8
9
10 1function *getObjectIterator(obj) {
2 for(let key in obj) {
3 yield obj[key];
4 }
5}
6const obj = { name: "cyl", age: 16 }
7for (let v of getObjectIterator(obj)) {
8 console.log(v);
9}
10
我们可以利用Generator和for of来实现一些复杂的遍历,而且不必向调用者暴露细节。