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 } 的对象。一旦 done 为 true,后续调用可忽略 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.prototype、String.prototype 等内置原型上已定义了 Symbol.iterator 方法;而 Object.prototype 没有——所以 {a:1, b:2} 直接用于 for...of 会报错。
使用场景:你想让自定义类/对象支持解构、for...of、Array.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...in 和 for...of 行为一致。











