怎么理解javascript的迭代器协议_它如何使对象可迭代?

Symbol.iterator 方法必须返回一个具有 next() 方法的对象,next() 每次调用需返回 { value: any, done: boolean } 结构;next 必须是普通函数以正确访问 this,且每次 for...of 遍历时都应返回新迭代器实例。

什么是迭代器协议?

迭代器协议是一组约定,让 JavaScript 引擎知道“这个对象能被 for...of 遍历”“能用扩展运算符 [...obj] 展开”——关键在于对象身上有没有一个叫 Symbol.iterator 的方法,且该方法返回一个符合迭代器接口的对象(即有 next() 方法)。

Symbol.iterator 方法必须返回什么?

它必须返回一个对象,该对象实现迭代器接口:至少带一个 next() 方法;next() 每次调用应返回形如 { value: any, done: boolean } 的对象。一旦 donetrue,后续调用可忽略 value 或直接复用上次值。

常见错误现象:TypeError: xxx is not iterable,往往是因为忘了在返回对象上挂 next,或 next 没返回正确结构。

  • next() 必须是函数,不能是箭头函数(否则无法访问 this 上的状态)
  • 多次调用 for...of 同一个对象,每次都会重新调用 Symbol.iterator,所以状态不应存在迭代器工厂外
  • 如果想支持多次遍历,Symbol.iterator 应每次返回**新**的迭代器实例,而不是复用同一个
const counter = {
  from: 1,
  to: 3,
  [Symbol.iterator]() {
    let current = this.from;
    return {
      next: () => {
        if (current <= this.to) {
          return { value: current++, done: false };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

[...counter]; // [1, 2, 3]

为什么数组、字符串天然可迭代,而普通对象不行?

因为 Array.prototypeString.prototype 等内置原型上已定义了 Symbol.iterator 方法;而 Object.prototype 没有——所以 {a:1, b:2} 直接用于 for...of 会报错。

使用场景:你想让自定义类/对象支持解构、for...ofArray.from() 等,就必须手动部署 Symbol.iterator

性能影响:只要不触发遍历,协议本身不消耗资源;但若在 Symbol.iterator 中做重计算(比如每次都 Object.keys(this)),就可能拖慢循环启动速度。

迭代器和生成器函数的关系?

生成器函数(function*)是创建迭代器的语法糖:它自动返回一个对象,该对象满足迭代器协议(自带 next()、还附带 return()throw())。你不用手动写 next 状态机逻辑。

容易踩的坑:yield 是暂停点,不是立即返回值;生成器函数本身不执行,只有调用其返回的迭代器的 next() 才开始运行并暂停在第一个 yield 处。

function* range(from, to) {
  for (let i = from; i <= to; i++) {
    yield i;
  }
}

const iter = range(1, 3); iter.next(); // { value: 1, done: false } iter.next(); // { value: 2, done: false } iter.next(); // { value: 3, done: false } iter.next(); // { value: undefined, done: true }

迭代器协议真正难的不是写法,而是理解“可迭代”不等于“有长度”或“有属性”,而是明确声明“我提供一种按需取值的序列”。很多 bug 来自把对象当数组用却没实现协议,或者误以为 for...infor...of 行为一致。