JavaScript迭代器是实现特定协议的对象,必须有next()方法返回{value, done}对象;for...of通过调用[Symbol.iterator]()获取迭代器并循环调用next()遍历,不直接操作数据结构。

JavaScript 迭代器不是语法糖,而是可被 for...of 消费的、有固定接口的对象;for...of 本身不直接遍历数组或字符串,它只调用对象的 [Symbol.iterator]() 方法获取迭代器,再反复调用其 next()。
迭代器对象长什么样?
一个合法迭代器必须满足:调用它的 next() 方法,返回形如 { value: any, done: boolean } 的对象。一旦 done 为 true,后续调用应始终返回 { value: undefined, done: true }。
常见错误现象:手动实现迭代器时忘记在末尾返回 done: true,导致 for...of 死循环。
-
next()必须是无参函数(不能带参数,即使某些场景如generator.next(value)支持传参,那是生成器协议的扩展,非基础迭代器) - 迭代器可以是“消耗型”的:同一迭代器多次调用
next()会推进内部状态,不可重用 - 数组、Map、Set、字符串等内置类型都默认实现了
[Symbol.iterator],所以能直接用于for...of
for...of 的实际执行步骤
它不关心数据结构,只依赖协议。执行时分三步:
立即学习“Java免费学习笔记(深入)”;
- 检查目标是否有
[Symbol.iterator]方法(存在且为函数) - 调用该方法,得到一个迭代器对象
- 循环调用该迭代器的
next(),直到返回的done为true
如果目标没有迭代器接口,会抛出 TypeError: xxx is not iterable。
示例:手动模拟 for...of 行为
const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
console.log(result.value); // 10 → 20 → 30
result = iterator.next();
}
自定义可迭代对象要注意什么?
只要在对象上定义 [Symbol.iterator] 方法,并让它返回一个符合迭代器协议的对象,就能被 for...of 消费。
容易踩的坑:
- 忘记用
Symbol.iterator字面量写法(不能写成字符串"Symbol.iterator") - 返回的不是对象,或对象缺少
next方法 - 在
next()中未正确维护内部索引或状态,导致重复或跳过值
简短示例:让普通对象支持 for...of
const obj = {
a: 1,
b: 2,
c: 3,
[Symbol.iterator]() {
const keys = Object.keys(this);
let index = 0;
return {
next: () => {
if (index < keys.length) {
return { value: this[keys[index++]], done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const val of obj) {
console.log(val); // 1 → 2 → 3
}
Generator 函数和迭代器的关系
Generator 函数(function*)调用后返回的正是一个迭代器对象,它天然符合协议 —— 所以 generator 是最省力的自定义迭代器方式。
但注意:generator 返回的迭代器,其 next() 支持传参,这是对基础协议的增强;而 for...of 永远不会传参给 next(),所以你无需在 generator 中处理传入值,除非你手动调用 next(value)。
性能提示:generator 有额外开销(闭包、状态机),简单遍历优先用数组方法或传统循环;只有需惰性求值、异步流控制、或复杂状态切换时,才值得用。
真正容易被忽略的是:for...of 对 undefined、null、普通对象(无 [Symbol.iterator])直接报错,它不像 for...in 那样“宽容”。别指望它能自动降级处理 —— 接口缺失就是缺失。











