JavaScript的Iterator是内置协议而非设计模式,但它为迭代器模式提供底层支持,通过Symbol.iterator统一集合遍历接口,实现封装细节与统一访问。

JavaScript 中的 Iterator(迭代器)本身不是设计模式,而是语言内置的协议(Symbol.iterator),但它天然支撑并简化了经典设计模式——迭代器模式(Iterator Pattern)的实现。关键在于:它把“如何遍历集合”的逻辑从客户端中解耦出来,让不同数据结构(数组、Map、自定义类等)对外暴露统一的遍历接口 next(),从而实现「统一集合访问」。
Iterator 协议是迭代器模式的底层基础设施
迭代器模式的核心目标是:提供一种方法顺序访问聚合对象(如列表、树、图)中的各个元素,而又不暴露其内部表示。JavaScript 的 Symbol.iterator 正好满足这一点:
- 任何对象只要实现了
[Symbol.iterator]()方法,并返回一个符合迭代器协议的对象(即有next()方法,返回{ value, done }),就可被for...of、展开运算符、解构等原生语法消费; - 客户端无需知道它是数组、Set 还是手写的链表——只要它“可迭代”,就能用相同方式遍历;
- 这正是迭代器模式所倡导的「封装遍历细节,提供统一接口」。
手动实现迭代器模式:为自定义集合添加 Symbol.iterator
比如你有一个简单的栈(Stack)类,想让它支持 for...of 遍历(从底到顶):
class Stack {
constructor() {
this.items = [];
}
push(item) { this.items.push(item); }
pop() { return this.items.pop(); }
<p>// 实现迭代器协议 → 即实现迭代器模式的关键一步
[Symbol.iterator]() {
let index = 0;
const items = this.items;
return {
next() {
if (index < items.length) {
return { value: items[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
}</p><p>// 使用:完全透明,和数组一样
const stack = new Stack();
stack.push('a'); stack.push('b'); stack.push('c');</p><p>for (const item of stack) {
console.log(item); // 'a', 'b', 'c'
}
更灵活的场景:支持多种遍历策略(多迭代器)
迭代器模式的优势之一是支持同一集合提供多种遍历方式(如正向/反向、深度优先/广度优先)。JavaScript 中可通过不同方法暴露不同迭代器:
立即学习“Java免费学习笔记(深入)”;
- 默认迭代器:
[Symbol.iterator](通常代表最常用遍历方式); - 自定义迭代器方法:
.values()、.keys()、.entries()(如 Map)、或自定义如.reverseIterator(); - 每个方法返回独立的迭代器对象,互不影响(状态隔离),符合迭代器模式的「游标(Cursor)」思想。
例如为栈添加反向迭代器:
class Stack {
// ... 其他代码同上
reverseIterator() {
let index = this.items.length - 1;
return {
next: () => {
if (index >= 0) {
return { value: this.items[index--], done: false };
}
return { value: undefined, done: true };
}
};
}
}
<p>// 使用
for (const item of stack.reverseIterator()) {
console.log(item); // 'c', 'b', 'a'
}
与传统迭代器模式对比:JS 的简洁性与限制
传统面向对象语言(如 Java)中,迭代器模式常需定义 Iterator 接口、具体迭代器类、聚合类的 createIterator() 方法。JavaScript 借助协议和函数式特性大幅简化:
- 无需接口声明,靠约定(
Symbol.iterator+next()); - 迭代器常以闭包形式实现,天然封闭状态,无需额外类;
- 但要注意:JS 迭代器是一次性的(
done: true后不再重置),若需多次遍历,每次都要调用[Symbol.iterator]()获取新迭代器——这反而更贴近模式本意(迭代器应代表一次遍历过程)。


















