迭代器模式
概念
用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
for 循环不是迭代器模式
简单的 for 循环并不是迭代器模式,因为 for 循环需要知道对象内部结构。
// 要知道数组的长度
// 要知道通过 arr[i] 形式得到item
const arr = [100, 20, 30];
const len = arr.length;
for (let i = 0; i < len; i++) {
console.log(arr[i]);
}
简易迭代器
有些对象,并不知道他的内部结构
// 不知道长度
// 不知道如何获取 item
const pList = document.querySelectAll('p');
pList.forEach((p) => console.log(p));
forEach 就是最简单的迭代器
UML 类图
代码演示
class DataIterator {
private data: number[];
private index = 0;
constructor(container: DataContainer) {
this.data = container.data;
}
next(): number | null {
if (this.hasNext()) {
return this.data[this.index++];
}
return null;
}
hasNext() {
if (this.index >= this.data.length) return false;
return true;
}
}
class DataContainer {
data: number[] = [10, 20, 30];
getIterator() {
return new DataIterator(this);
}
}
const container = new DataContainer();
const iterator = container.getIterator();
while (iterator.hasNext()) {
const num = iterator.next();
console.log(num);
}
场景
JS 有序对象,都内置迭代器
- 字符串
- 数组
- NodeList 等 DOM 集合
- Map
- Set
- arguments
【注意】对象 object 不是有序结构。
Symbol.iterator
每个有序对象,都内置了 Symbol.iterator 属性,属性值是一个函数。 执行该函数将返回 iterator 迭代器,有 next() 方法,执行返回 { value, done } 结构。
// 数组举例
const arr = [1000, 20, 30];
const iterator = arr[Symbol.iterator]();
iterator.next(); // {value: 1000, done: false}
iterator.next(); // {value: 20, done: false}
iterator.next(); // {value: 30, done: false}
iterator.next(); // {value: undefined, done: true}
另外,有些对象的 API 也会生成有序对象
const map = new Map([
['k1', 'v1'],
['k2', 'v2'],
]);
const mapIterator = map[Symbol.iterator]();
const values = map.values();
const valuesIerator = values[Symbol.iterator]();
// 还有 keys entries
自定义迭代器
// demo06/iterator.ts
// 自定义迭代器
interface IteratorRes {
value: number | undefined;
done: boolean;
}
class CustomIterator {
private length = 3;
private index = 0;
next(): IteratorRes {
this.index++;
if (this.index <= this.length) {
return { value: this.index, done: false };
}
return { value: undefined, done: true };
}
[Symbol.iterator]() {
return this;
}
}
const iterator = new CustomIterator();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
export {};
// demo06/yield.ts
class CustomIterator {
private data: number[];
constructor() {
this.data = [100, 20, 30];
}
*[Symbol.iterator]() {
yield* this.data;
}
}
const iterator = new CustomIterator();
for (const n of iterator) {
console.log(n);
}
export {};
有序结构的作用
for...of
所有有序结构,都支持 for...of 语法
数组操作
// 数组解构
const someList = [10, 20];
const [n1, n2] = someList;
// 拓展操作符
const arr = [...someList];
// Array.from()
const arr = Array.from(someList);
创建 Map 和 Set
const map = new Map([
['k1', 'v1'],
['k2', 'v2'],
]);
const set = new Set(someList);
Promise.all 和 Promise.race
Promise.all([promise1, promise2, promise3]);
Promise.race([promise1, promise2, promise3]);
Generator 生成器
yield* 操作符
function* genNums() {
yield 10;
yield 20;
yield 30;
}
const numsIterator = genNums(); // 迭代器,如 arr[Symbol.iterator]()
console.log(numsIterator.next());
for (let n of numsIterator) {
console.log(n);
}
function* genNums() {
yield* [10, 20, 30]; // 有序结构,已经实现了 Symbol.iterator
// => [10,20,30].forEach(n => yield n);
}
const numsIterator = genNums();
for (let n of numsIterator) {
console.log(n);
}
使用 Generator 遍历 DOM 树
function* traverse(elemList: Element[]): any {
for (const elem of elemList) {
yield elem;
const children = Array.from(elem.children);
if (children.length) {
yield* traverse(children);
}
}
}
const container = document.getElementById('container');
if (container) {
for (let node of traverse([container])) {
console.log(node);
}
}
设计原则验证
- 使用者和目标数据隔离,解耦
- 目标数据自行控制内部迭代逻辑
- 使用者不关心目标数据的内部结构
- 符合开放封闭原则