for...in遍历对象可枚举属性名(如数组下标字符串),基于[[Enumerate]];for...of遍历可迭代对象的实际值,依赖Symbol.iterator协议,二者底层机制不同。

for...in 遍历的是对象的**可枚举属性名(通常是字符串索引)**,for...of 遍历的是可迭代对象的**实际值(元素本身)**。这个区别不是语法糖差异,而是底层协议不同:前者基于 [[Enumerate]],后者依赖 Symbol.iterator。
for...in 本质是“查键”,不是“查数”
它不关心数据类型是否为数组,只看对象有哪些可枚举属性。对数组使用时,拿到的是字符串形式的下标(如 "0"、"1"),还可能遍历到原型链上新增的可枚举属性(比如给 Array.prototype 添加了方法)。
- 对空数组
[],for...in不执行循环体(无属性) - 对稀疏数组
[,,2],会遍历到 "2"(即索引 2),跳过缺失项,但不会报错 - 若执行了
Array.prototype.foo = 'bar',再用for...in遍历任意数组,很可能输出 "foo"
for...of 本质是“取值”,要求对象可迭代
它调用对象的 [Symbol.iterator]() 方法获取迭代器,然后反复调用 next() 取值,直到 done: true。数组、字符串、Map、Set、TypedArray 等原生类型都实现了该协议。
- 对数组
[10, 20, 30],直接得到10→20→30,没有索引信息 - 对普通对象
{a:1, b:2},直接使用for...of会报错:TypeError: is not iterable - 想让对象支持 for...of,必须手动部署
[Symbol.iterator]方法
需要索引又想要值?别硬套,用更合适的工具
强行在 for...in 里用 arr[key] 取值,或在 for...of 里靠闭包计数,都不直观也不安全。现代 JavaScript 提供了明确语义的替代方案:
-
arr.entries()→ 同时获得索引和值:for (const [i, v] of arr.entries()) { ... } -
arr.keys()→ 只要索引:for (const i of arr.keys()) { ... } -
arr.values()→ 只要值(等价于直接for...of arr) - 普通循环仍需索引逻辑?
for (let i = 0; i 最直接、兼容性最好
一句话总结关键分界点
for...in 是面向“对象结构”的反射式遍历(你问“它有什么键?”),for...of 是面向“数据消费”的协议式遍历(你问“它能提供什么值?”)。选哪个,取决于你要回答的问题——是查结构,还是取内容。










