迭代器模式
行为型模式,普通的 for 循环不属于迭代器模式
特点
迭代器模式的核心在于抽象遍历过程,使得客户端无需关心底层数据结构的具体实现。
- 顺序访问有序结构(如数组,NodeList)
- 不知道数据的长度和内容结构
- 高内聚、低耦合
代码
ts
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 = [10, 20, 30, 40, 50];
getIterator() {
// 返回一个迭代器对象
return new DataIterator(this);
}
}
const container = new DataContainer();
const iterator = container.getIterator();
while (iterator.hasNext()) {
console.log(iterator.next());
}使用 UML 图表示

下面看一下使用场景
有序结构
有序结构是迭代器模式的主要应用场景,以下是一些常见的有序结构:
- 字符串
- 数组(最常见的有序结构,迭代器模式可以统一遍历方式)
- NodeList 等 DOM 集合
- Map
- Set
- 函数的 arguments 对象
Symbol.iterator 和迭代器
Symbol.iterator 是 JavaScript 中实现迭代器模式的标准化接口。
- 所有有序对象,都内置了 Symbol.iterator 方法
- 执行该方法,会返回一个迭代器对象
js
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
iterator.next();- 模拟 Symbol.iterator 方法
ts
interface InteratorRes {
value: number | undefined;
done: boolean;
}
class CustomIterator {
private length = 3;
private index = 0;
next(): InteratorRes {
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());
console.log(iterator.next());迭代器的作用
- 用于 for...of 循环(对比 for...in)
js
for (let n of iterator) {
console.log(n);
}- 数组:解构,扩展操作符,Array.from()
js
// 解构
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 扩展操作符
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
console.log(newArr); // [1, 2, 3, 4, 5]
// Array.from()
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
const arrFrom = Array.from(arrayLike);
console.log(arrFrom); // ['a', 'b']- 用于创建 Map 和 Set 等数据结构
- 用于 Promise.all 和 Promise.race 等方法
js
const arr = [1, 2, 3];
const map = new Map(arr);
Promise.all(map);- 用于 yield* 语句
Generator 生成器
基本使用
ts
function* genNums() {
yield 10;
yield 20;
yield 30;
}
const numsItr = genNums(); // 迭代器,类似 arr[Symbol.iterator]()
// console.log(numsItr.next());
// console.log(numsItr.next());
// console.log(numsItr.next());
// console.log(numsItr.next());
for (const num of numsItr) {
console.log(num);
}yield* 语句
ts
function* genNums() {
yield* [10, 20, 30]; // * 后可以带有序结构,已经实现 [Symbol.iterator]
// 或者
// for(const num of [10,20,30]){
// yield num;
// }
}
const numsItr = genNums(); // 迭代器,类似 arr[Symbol.iterator]()
console.log(numsItr.next());
console.log(numsItr.next());
console.log(numsItr.next());
console.log(numsItr.next());
// for(const num of numsItr){
// console.log(num);
// }改造上面的 CustomIterator 方法
ts
class CustomIterator {
private data: number[];
constructor() {
this.data = [10, 20, 30, 40, 50];
}
*[Symbol.iterator]() {
yield* this.data;
}
}
const iterator = new CustomIterator();
for (const n of iterator) {
console.log(n);
}Generator + yiled 遍历 DOM 树
ts
function* traverse(elList: Element[]): any {
for (const el of elList) {
yield el;
const children = Array.from(el.children);
if (children.length) {
yield* traverse(children);
}
}
}
const elList = document.getElementById('container');
if (elList) {
for (const el of traverse([elList])) {
console.log(el);
}
}