最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何通过自定义迭代器Symbol.iterator实现可遍历的类实例
时间:2026-06-05 10:04:47 编辑:袖梨 来源:一聚教程网
要让类实例支持for...of等遍历,需定义[Symbol.iterator]方法返回符合迭代器协议的对象;若需异步遍历,则实现[Symbol.asyncIterator]方法。
要让一个类实例能被 for...of、扩展运算符([...obj])、解构等语法遍历,关键是在该实例上定义 [symbol.iterator] 方法,返回一个符合迭代器协议的对象(即有 next() 方法,返回 { value, done } 形式的对象)。
1. 基础实现:返回简单值序列
最常见场景是让实例按某种顺序“产出”内部数据(如数组、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']
2. 支持多种遍历模式:用方法参数控制行为
如果希望同一实例支持不同遍历方式(如只遍历键、只遍历值、或键值对),可把 [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()),它天然返回迭代器,写法更简洁、状态自动管理,推荐优先使用。
3. 注意事项与常见陷阱
-
必须返回迭代器对象:不是直接返回值,也不是返回普通对象;必须有
next()方法,且每次调用返回{ value, done } -
done 为 true 后仍可调用 next():规范要求后续调用应始终返回
{ value: undefined, done: true },需在逻辑中处理越界情况 -
避免闭包引用外部可变状态出错:若迭代器依赖实例属性(如
this.items),而该属性在遍历中途被修改,行为可能不符合预期;必要时可提前快照(如const items = [...this.items]) -
生成器函数是首选:比手写
next()更安全、易读、支持yield*、异步迭代(async function*)等高级特性
4. 进阶:支持异步迭代(AsyncIterator)
若数据源是异步的(如从 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) { ... }
相关文章
- 伊莫星骑士支线任务如何完成 06-16
- 逆战未来深渊狂潮怎么玩 06-16
- 银河灰暗角落结局彩蛋触发方法分享 06-16
- 异能重组护盾流玩法攻略介绍说明 06-16
- 别拽了烤串师傅气味炸弹成就解锁攻略 06-16
- 银河灰暗角落暴击流玩法构筑分享 06-16