JavaScript迭代器模式通过Symbol.iterator提供统一遍历接口,解耦存储与访问;支持同步(生成器实现惰性遍历)和异步(Symbol.asyncIterator)场景,提升可维护性与可控性。

JavaScript 中的迭代器模式,核心是让复杂数据结构(如树、图、嵌套对象、自定义集合)对外提供统一、可控的遍历接口,而不暴露内部实现细节。它不是语法糖,而是解耦“如何存储”和“如何访问”的关键设计。
用 Symbol.iterator 定义可迭代行为
任何对象只要部署了 Symbol.iterator 方法,并返回一个符合迭代器协议的对象(即有 next() 方法,返回 { value, done } 形式的结果),就能被 for...of、扩展运算符、Array.from 等原生语法识别。
例如,为一个深度嵌套的配置对象实现扁平化遍历:
const config = {
db: { host: 'localhost', port: 3306 },
cache: { enabled: true, ttl: 300 },
features: { auth: true, logging: false }
};
<p>config[Symbol.iterator] = function<em> () {
const flatten = (obj, prefix = '') => {
for (let [k, v] of Object.entries(obj)) {
const key = prefix ? <code>${prefix}.${k}</code> : k;
if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
yield</em> flatten(v, key);
} else {
yield [key, v];
}
}
};
yield* flatten(this);
};</p><p>// 现在可以这样用
for (const [path, value] of config) {
console.log(<code>${path} = ${value}</code>);
}
// 输出:db.host = localhost,db.port = 3306,cache.enabled = true……
为树形结构实现惰性、按需遍历
树的遍历(如前序、中序、后序)若一次性生成全部节点数组,会浪费内存且无法中断。用生成器函数实现迭代器,天然支持惰性求值和 early exit。
立即学习“Java免费学习笔记(深入)”;
- 每个节点只需在被访问时才计算子节点,适合超大 DOM 树或 AST 遍历
- 可在 for...of 中用 break/return 提前终止,无需遍历整棵树
- 配合 yield* 可轻松组合多个子树的遍历逻辑
示例:二叉树中序遍历迭代器
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
<p><em>[Symbol.iterator]() {
if (this.left) yield</em> this.left;
yield this.val;
if (this.right) yield* this.right;
}
}</p><p>const root = new TreeNode(1,
new TreeNode(2, new TreeNode(4), new TreeNode(5)),
new TreeNode(3)
);</p><p>for (const val of root) {
console.log(val); // 4 → 2 → 5 → 1 → 3
}
封装异步数据流为可迭代接口
迭代器本身是同步的,但可通过 async iterator(Symbol.asyncIterator)处理分页列表、EventSource、WebSocket 消息流等异步序列。它返回一个 next() 返回 Promise 的对象,配合 for await...of 使用。
常见场景:
- 分页 API 调用:每次 next() 自动拉取下一页,使用者只关心“取下一个”,不操心翻页逻辑
- 日志流消费:服务端持续推送,客户端用迭代器逐条处理,自动重连与错误恢复可封装在迭代器内部
- 避免回调地狱或手动管理 Promise 链,语义更清晰
简单示意:
class PaginatedAPI {
constructor(url) {
this.url = url;
this.page = 1;
}
<p>async next() {
const res = await fetch(<code>${this.url}?page=${this.page}</code>);
const data = await res.json();
if (data.length === 0) return { value: undefined, done: true };
this.page++;
return { value: data, done: false };
}</p><p>[Symbol.asyncIterator]() {
return this;
}
}</p><p>// 使用
for await (const page of new PaginatedAPI('/api/items')) {
console.log('Received page:', page);
}
与生成器组合实现状态化遍历逻辑
生成器函数(function*)是创建迭代器最自然的方式。它能暂停执行、保存上下文,特别适合需要维护遍历状态的场景,比如:带过滤条件的多级嵌套遍历、回溯路径记录、带计数/索引的遍历等。
例如:遍历文件系统树,只返回 .js 文件,并附带层级深度
function* walkDir(node, depth = 0) {
if (node.type === 'file' && node.name.endsWith('.js')) {
yield { path: node.path, depth };
}
if (node.type === 'dir') {
for (const child of node.children) {
yield* walkDir(child, depth + 1);
}
}
}
<p>// 外部调用者完全不用知道递归细节,只按需取值
for (const { path, depth } of walkDir(rootNode)) {
console.log(<code>${' '.repeat(depth)}→ ${path}</code>);
}
迭代器模式的价值不在“炫技”,而在于把遍历逻辑从使用方抽离,让数据结构专注表达关系,让业务代码专注处理数据。它让复杂变简单,让不可控变可控,是构建可维护、可组合、可测试遍历能力的底层支撑。










