Menu

JavaScript for...of 与 for...in:遍历数组和对象

JavaScript 中 for...of 和 for...in 到底有什么不同:值与键、数组与对象,以及各自该在什么时候用。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

两种循环,两种不同的用途

JavaScript 有两种看起来几乎一模一样、作用却截然不同的循环。for...of 遍历可迭代对象的值,for...in 遍历对象的键。只差一个单词,行为却天差地别。

一个例子就能说清全部:

第一个循环打印 apple、banana、cherry,也就是值。第二个打印 0、1、2,也就是键,而且是字符串。选错了的话,你可能要花十分钟琢磨为什么数组里全是数字。

for...of:从任何可迭代对象中取值

for...of 是你最常用的循环。它适用于 JavaScript 认为可迭代的任何东西:数组、字符串、Map、Set、NodeList、生成器等等。

不用摆弄索引,也不用写 scores[i]。你要每个值,就得到每个值。字符串也是可迭代的,for...of 会逐个字符地遍历它:

这对大多数 Unicode 码点也能正确处理,比起用数字下标访问字符串,这是一个虽小却实在的优势。

用 for...of 获取索引

for...of 唯一不会直接给你的就是位置。需要它时,把循环和 entries() 搭配使用:

names.entries() 会产出 [index, value] 对,左边的解构把它们拆成两个变量。这通常比仅仅为了得到 i 就退回到经典的 for (let i = 0; ...) 更简洁。

for...in:对象的键

for...in 是为普通对象设计的。它遍历可枚举的字符串键:

注意你得到的是键,而不是值。要读取值,需要用 user[key] 回到对象中取。每个键都是字符串,即使它看起来像数字。

for...in 还会沿着原型链向上查找,所以可能会遍历到继承来的属性。对于你自己写的字面量对象,这很少有影响;但在遍历类的实例或来自库的对象时,最好多加防范:

Object.hasOwn(user, key) 会跳过所有继承的属性。在现代代码中,大多数人完全不用 for...in,而是改用 Object.keys、Object.values 或 Object.entries,这正是下一节的内容。

遍历对象的现代写法

不用 for...in,而是把 for...of 和某个 Object.* 辅助方法结合起来。三个方法中选一个符合需要的即可:

Object.entries 尤其好用:把 [key, value] 对解构出来,读起来就像普通的句子。而且这些方法只返回对象自身的可枚举属性,你不用担心继承来的杂七杂八。

不要对数组使用 for...in

技术上是合法的,但很容易出问题:

你会得到 0、1、2,还有 tag。任何人挂到这个数组上的属性,或者通过 polyfill 挂到 Array.prototype 上的属性,都会出现。键是字符串,所以 key + 1 会变成字符串拼接而不是加法。而且在某些边界情况下,遍历顺序不能保证和数组顺序一致。

经验法则:

  • 数组的值?for...of arr。
  • 数组的索引和值?for...of arr.entries()。
  • 只要数组索引、按数计数?经典的 for (let i = 0; i < arr.length; i++)。
  • 对象的键或键值对?Object.keys(obj) / Object.entries(obj) 配合 for...of。

总之:for...in 是个小众工具。for...of 加上 Object 辅助方法就能满足你的所有需要。

break 和 continue 在两者中都能用

两种循环都支持常用的提前退出手段:

continue 跳到下一次迭代;break 完全退出循环。这也是优先选择 for...of 而不是 .forEach() 的主要原因:forEach 的回调无法用 break 跳出循环,而 for...of 可以。

对比总结

四种循环,四种用途,一个思维模型:想从可迭代对象中取值,就用 for...of。想处理对象的属性,就通过 Object.keys / Object.values / Object.entries,同样配合 for...of。只有在极少数确实需要对象的每个可枚举字符串键(无论是否继承)的情况下,才用 for...in。

下一步:真值和假值

JavaScript 中的每个循环和 if 最终都会问同一个问题:这个值算不算真?答案并不总是显而易见:空字符串、零、null 和 undefined 的表现都可能出乎你的意料。下一页讲真值和假值。

常见问题

JavaScript 中 for...of 和 for...in 有什么区别?

for...of 遍历可迭代对象(如数组、字符串、Map 或 Set)的值。for...in 遍历对象的键(属性名)。对于数组 ['a', 'b'],for...of 得到的是 'a' 和 'b';for...in 得到的是字符串形式的 '0' 和 '1'。

可以对对象使用 for...of 吗?

不能直接用,普通对象不是可迭代的。先用 Object.keys(obj)、Object.values(obj) 或 Object.entries(obj) 得到一个数组,再用 for...of 遍历。常见写法是 for (const [key, value] of Object.entries(obj))。

为什么不应该对数组使用 for...in?

技术上可行,但 for...in 会遍历每个可枚举属性,包括继承来的属性,以及别人添加到 Array.prototype 上的任何额外属性。它返回的键也是字符串,而且对于类似整数的键,并不是在所有情况下都能完全保证顺序。要取值就用 for...of,需要索引时用经典的 for 循环。

Coddy 编程语言插图

和 Coddy 一起学编程

开始