普通对象不能直接用 for-of 遍历,根本原因是未实现可迭代协议(即缺少 Symbol.iterator 方法);for-of 依赖该方法获取迭代器并调用 next(),而对象作为无序属性容器,语言未预设遍历语义。

普通对象不能直接用 for-of 遍历,根本原因在于它没实现可迭代协议(Iterable Protocol)——即缺少 [Symbol.iterator] 方法。这不是语言限制,而是设计选择:对象本质是无序的属性容器,没有预设“该按什么顺序、遍历什么内容”的语义,所以引擎不会默认赋予它迭代能力。
为什么 for-of 要求对象必须有 Symbol.iterator?
for-of 的底层逻辑是:先调用对象的 [Symbol.iterator]() 方法,拿到一个迭代器对象;再反复调用该迭代器的 next() 方法,直到 done: true。如果对象没有这个方法,运行时就会报错 TypeError: is not iterable。
- 数组、Map、Set、字符串等内置类型都自带
[Symbol.iterator],所以天然支持for-of - 普通对象(如
{a: 1, b: 2})原型链上没有这个方法,因此不满足协议 - 即使对象有
length或看起来像数组,只要没实现该符号方法,for-of就无法识别
如何给对象添加 Symbol.iterator 实现 for-of 遍历
最常用的方式是让对象在 for-of 中按 [key, value] 形式逐对遍历,模拟 Object.entries() 的行为:
const obj = { x: 10, y: 20, z: 30 };
obj[Symbol.iterator] = function* () {
for (let key of Object.keys(this)) {
yield [key, this[key]];
}
};
<p>// 现在可以这样用:
for (let [k, v] of obj) {
console.log(k, v); // x 10, y 20, z 30
}
console.log([...obj]); // [['x', 10], ['y', 20], ['z', 30]]
注意:这是为单个对象实例添加方法。若想批量生效,可挂载到 Object.prototype 上(但不推荐,会污染全局环境);更稳妥的做法是封装成工具函数或使用类继承。
立即学习“Java免费学习笔记(深入)”;
灵活控制遍历内容:键、值或键值对
不必只依赖 Symbol.iterator。你可以定义多个命名方法,按需调用:
- 只遍历值:
obj.values = function*() { yield* Object.values(this); }; - 只遍历键:
obj.keys = function*() { yield* Object.keys(this); }; - 遍历键值对:
obj.entries = function*() { yield* Object.entries(this); };
这样写既清晰又不干扰默认迭代行为,调用时也明确:for (let v of obj.values())、for (let k of obj.keys())。
替代方案:不用改原型也能遍历
多数场景下,不需要手动加迭代器。直接用标准 API 更简洁安全:
- 遍历键:
for (let k of Object.keys(obj)) { ... } - 遍历值:
for (let v of Object.values(obj)) { ... } - 遍历键值对:
for (let [k, v] of Object.entries(obj)) { ... }
这些方法返回的都是真正的可迭代数组(或类数组),天然兼容 for-of、扩展运算符和解构。


















