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