要让类实例支持for...of等遍历,需定义[Symbol.iterator]方法返回符合迭代器协议的对象;若需异步遍历,则实现[Symbol.asyncIterator]方法。
要让一个类实例能被 for...of、扩展运算符([...obj])、解构等语法遍历,关键是在该实例上定义 [symbol.iterator] 方法,返回一个符合迭代器协议的对象(即有 next() 方法,返回 { value, done } 形式的对象)。
最常见场景是让实例按某种顺序“产出”内部数据(如数组、Map 的键/值等)。只需在类的原型或实例上定义 [Symbol.iterator],返回一个闭包内的迭代器函数即可:
示例:遍历类内部维护的 items 数组
class Collection { constructor(items = []) { this.items = items; } [Symbol.iterator]() { let index = 0; return { next: () => { if (index < this.items.length) { return { value: this.items[index++], done: false }; } return { value: undefined, done: true }; } }; }}const coll = new Collection(['a', 'b', 'c']);for (const item of coll) { console.log(item); // 'a', 'b', 'c'}console.log([...coll]); // ['a', 'b', 'c']
如果希望同一实例支持不同遍历方式(如只遍历键、只遍历值、或键值对),可把 [Symbol.iterator] 设为返回一个工厂函数,并通过实例方法暴露不同迭代器:
[Symbol.iterator] 中硬编码逻辑,而是委托给一个私有方法(如 _getIterator(mode))[Symbol.iterator] 默认返回一种模式(如 values),其他模式通过显式方法调用(.keys()、.entries())获取next() 返回 { value, done }
示例:模拟 Map 的遍历接口
class MyMap { constructor(entries = []) { this._data = new Map(entries); } [Symbol.iterator]() { return this.values(); // 默认遍历值 } *keys() { for (const key of this._data.keys()) { yield key; } } *values() { for (const value of this._data.values()) { yield value; } } *entries() { for (const entry of this._data.entries()) { yield entry; } }}
注意:这里用了生成器函数(function* 或 *method()),它天然返回迭代器,写法更简洁、状态自动管理,推荐优先使用。
next() 方法,且每次调用返回 { value, done }
{ value: undefined, done: true },需在逻辑中处理越界情况this.items),而该属性在遍历中途被修改,行为可能不符合预期;必要时可提前快照(如 const items = [...this.items])next() 更安全、易读、支持 yield*、异步迭代(async function*)等高级特性若数据源是异步的(如从 API 分页加载),可实现 [Symbol.asyncIterator],返回一个异步迭代器(next() 返回 Promise<{ value, done }>):
class AsyncCollection { constructor(fetcher) { this.fetcher = fetcher; // (page) => Promise<items[]> } async *[Symbol.asyncIterator]() { let page = 1; while (true) { const items = await this.fetcher(page); if (items.length === 0) break; for (const item of items) { yield item; } page++; } }}// 使用:// for await (const item of asyncColl) { ... }