for-of循环仅适用于实现了[Symbol.iterator]方法的可迭代对象;数组、字符串、Map等原生可迭代,普通对象默认不可迭代,需用Object.keys()等转换或手动部署[Symbol.iterator]。

for-of 循环只能遍历实现了 [Symbol.iterator] 的对象
不是所有 JS 对象都能用 for...of 遍历。它只认「可迭代对象(iterable)」——即内部有 [Symbol.iterator] 方法、且该方法返回一个符合迭代器协议的对象。数组、String、Map、Set、TypedArray、arguments、NodeList 是原生可迭代的;普通对象({})默认不是。
常见错误:直接对 plain object 写 for...of,会报 TypeError: xxx is not iterable。
- 想遍历对象键值?用
Object.keys(obj)、Object.entries(obj)包一层再 for-of - 自定义类要支持 for-of?必须在原型上部署
[Symbol.iterator]方法 - Node.js 早期版本(Map.prototype.values() 返回值)可能不被 for-of 直接识别,需确认运行时环境
for...of 和 for...in 的根本区别在哪
for...in 遍历的是对象的**可枚举属性名(字符串 key)**,包括原型链上的;for...of 遍历的是对象的**迭代值(value)**,只走自身实现的迭代协议,与属性名无关。
典型陷阱:for...in 遍历数组会得到索引字符串("0", "1"),还可能混入添加的非数字属性;而 for...of 拿到的是真实元素值(arr[0], arr[1]),更安全直观。
立即学习“Java免费学习笔记(深入)”;
-
for...in适合查对象结构(比如调试时看有哪些字段) -
for...of适合取数据(尤其配合break/continue或await使用) - 两者都不能保证遍历顺序对所有对象一致,但对数组、字符串、Map、Set,ES2015+ 规范已明确要求按插入顺序
如何让自定义对象支持 for...of
只需在对象(或其原型)上定义 [Symbol.iterator] 方法,返回一个对象,该对象有 next() 方法,每次调用返回 { value, done } 形式的迭代结果。
const counter = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const num of counter) {
console.log(num); // 1, 2, 3
}
-
next()必须是无参函数;带参数会被忽略(除非是 generator,但那是另一套机制) -
done: true后,后续调用next()应始终返回{ value: undefined, done: true } - 如果想复用逻辑,可以把迭代器逻辑抽成独立函数,多个对象共用
遇到 TypeError: xxx is not a function 怎么排查
这个错误常出现在你误以为某个对象可迭代,但它返回的 [Symbol.iterator] 是 undefined 或非函数。例如:DOM 元素集合在低版本浏览器中可能没正确暴露迭代器;或你手动删了 Array.prototype[Symbol.iterator](极少见但可能)。
快速验证方式:
const obj = /* 你的对象 */; console.log(typeof obj[Symbol.iterator]); // 应该是 "function" console.log(obj[Symbol.iterator]()); // 应该返回一个有 next() 的对象
- Node.js 中检查是否启用了
--harmony-iterator(v12 以前旧版本需要) - 使用 Babel 编译时,确保
@babel/preset-env配置了目标环境,否则可能把for...of转成不兼容的代码 - 某些库(如早期 Lodash)的包装对象(
_.wrap()等)不自动继承迭代器,需显式调用.value()获取底层数组再遍历
迭代器协议看着抽象,实际就是「提供一个标准接口让 for-of 知道怎么一步步取值」。真正容易卡住的地方,往往不是写错语法,而是没意识到某个对象根本没实现这个接口,或者环境不支持。











