一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何通过自定义迭代器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) { ... }

热门栏目