for...in 和 for...of 的终极区别:一文彻底解决你的困惑

JavaScript 中的 for...infor...of,名字像,功能却天差地别。

混用?代码能跑但极易出 bug。

想记住?咬牙背十次不如一次理解 + 巧记 + 联想。

本篇一文到底,帮你搞懂它们的区别、用途、坑点和记忆技巧。


📦 一张总表,快速建立概念框架

项目 for...in for...of
遍历的目标 对象、数组(但不推荐) 可迭代对象(数组、字符串、Set、Map 等)
遍历的内容 键名(属性名) 值(元素内容)
本质机制 枚举对象的"可枚举属性" 调用 **[Symbol.iterator]()** 拿值
是否包含原型链? ✅ 会遍历原型链 ❌ 不会
是否能保证顺序? ❌ 不保证 ✅ 保证迭代顺序
适合对象遍历? ✅ 是 ❌ 否(对象不是可迭代的)
适合数组遍历? ❌ 慎用,key 是字符串索引 ✅ 推荐,直接遍历值
常见误用风险 多遍历出 prototype 的属性 无法遍历对象,会直接抛错

📌 二、字面理解 ≠ 真正语义:哪里容易误导?

许多人初学时会以为:

  • "in 是在容器里面遍历,应该遍历的是"
  • "of 是属于什么东西,应该遍历的是键名"

👉 其实 完全相反

巧计: 看字母最后一位!

语句 最后一个字母 联想
for...in n n = name = 键名
for...of f f = from = 值是从里面来的

🔑 所以------

  • for...**in** 遍历的是:key(名字)
  • for...**of** 遍历的是:value(值)

🎯 三、实战演练对比:代码最能说明问题

✅ 正确使用 for...in 遍历对象属性:

javascript 复制代码
const obj = { name: '小吴', age: 28 };
for (const key in obj) {
  console.log(`${key}: ${obj[key]}`);
}
// 输出: name: 小吴,age: 28

🚨 不推荐使用 for...in 遍历数组:

ini 复制代码
Array.prototype.extra = '扩展';
const arr = ['🍎', '🍌'];

for (const index in arr) {
  console.log(index); // 输出:'0', '1', 'extra' ❌
}

📉 为什么错?因为:

  • for...in 会遍历到原型上的可枚举属性

  • 遍历的是 字符串索引('0', '1') ,不是数字

  • 数组本质上是对象:

    • 数组是一种拥有特殊键(数字索引)和 length 属性的对象。
    • 数组中的索引如 0, 1, 2 都是对象的属性名,实质上是 "0", "1", "2" 这些字符串键。
  • for...in 遍历的是对象的可枚举属性名(字符串),包括数组的索引。


✅ 正确使用 for...of 遍历数组:

arduino 复制代码
const arr = ['🍎', '🍌', '🍉'];

for (const fruit of arr) {
  console.log(fruit); // 直接输出值:🍎 🍌 🍉
}

for...of 可遍历字符串、Set、Map:

javascript 复制代码
for (const ch of 'Hello') console.log(ch); // H e l l o

for (const item of new Set([1, 2, 3])) console.log(item); // 1 2 3

for (const [key, val] of new Map([['a', 1], ['b', 2]]))
  console.log(key, val); // a 1, b 2

🧠 四、记忆技巧:一次记牢

✅ 技巧 1:看字母结尾

  • **in** 结尾是 n → name(键名)
  • **of** 结尾是 f → from(从容器中拿值)

✅ 技巧 2:角色隐喻法(不剧情)

语法 角色类比 它关注什么?
for...in 图书管理员 看"书名"(key)
for...of 图书借阅员 直接"翻书内容"(value)

✅ 技巧 3:出场规则口诀

"in 看名,of 看值;in 查对象,of 点人头。"


🔬 五、底层机制区别

for...in 本质是这样实现的:

vbnet 复制代码
const keys = [];
for (let key in obj) {
  keys.push(key); // 只抓 key,不抓 value
}
  • 只遍历可枚举属性
  • 包括继承来的原型链上的属性

for...of 背后的核心是迭代器:

ini 复制代码
const iterable = ['a', 'b'];
const iterator = iterable[Symbol.iterator]();
console.log(iterator.next()); // { value: 'a', done: false }

所有可迭代对象都必须实现 [Symbol.iterator]() 方法

注意:!!!所以对象默认不是可迭代的

若你想对对象用 for...of,请先转为可迭代结构 (如 entries()、自定义 iterator、Map)。


🧪 六、典型错误总结(能避坑)

错误代码 错因 替代方案
for (x in [1, 2]) 遍历字符串索引 + 可能包含原型属性 改用 for...of
for (x of { a: 1 }) 对象不是 iterable,会直接报错 Object.entries() + for...of
遍历 Map 不解构 for (item of map) item 是 [key, value] 数组 使用 [k, v] of map

📌 七、终极总结

特性 for...in for...of
遍历对象 ✅ 适用 ❌ 不适用
遍历数组 ❌ 慎用 ✅ 推荐
遍历值 ❌ 键名而非值 ✅ 真正的元素值
原型链属性 ✅ 会遍历 ❌ 不会
是否迭代器 ❌ 否 ✅ 是

🎁 八、小结金句(写进脑海)

rust 复制代码
for in 查的是"键名"(name);
for of 取的是"值"(from容器中);
in 针对对象属性;
of 针对迭代结构;

🧪 配套练习题:从易到难,一网打尽


✅ 基础题 1:这段代码输出什么?

ini 复制代码
const arr = ['a', 'b', 'c'];
for (let key in arr) {
  console.log(key);
}

🧠 你的答案?

解析:

  • for...in 遍历的是"键名"
  • 输出的是:'0' '1' '2'(注意是字符串)

✅ 基础题 2:这段代码输出什么?

ini 复制代码
const arr = ['a', 'b', 'c'];
for (let val of arr) {
  console.log(val);
}

输出: a b c

因为 for...of 直接取值。


⚠️ 中级题 3:输出什么?

ini 复制代码
const arr = ['x'];
arr.prop = 'extra';

for (let k in arr) console.log('in:', k);
for (let v of arr) console.log('of:', v);

输出:

makefile 复制代码
in: 0
in: prop
of: x

🎯 考点解析:

  • for...in 遍历可枚举属性,包括 prop(添加的自定义属性)
  • for...of 只遍历值(只看 [0] = 'x'

⚠️ 进阶题 4:报错 or 正常?

ini 复制代码
const obj = { a: 1, b: 2 };
for (let val of obj) {
  console.log(val);
}

会报错!

🧠 因为:对象不是可迭代的(没有 [Symbol.iterator]

✅ 正确方式:

javascript 复制代码
for (let [k, v] of Object.entries(obj)) {
  console.log(k, v);
}

💡 面试题 5:以下输出结果是?

ini 复制代码
Object.prototype.extra = '原型';

const obj = { name: '小吴', age: 18 };
for (let key in obj) {
  console.log(key);
}

输出:

复制代码
name
age
extra

🎯 原因:

  • for...in 会遍历原型链上的可枚举属性
  • 这就是它不适合在数组中使用的原因之一

🧨 面试陷阱题 6:

ini 复制代码
const arr = [1, 2, 3];
arr.custom = 'x';

for (let i of arr) console.log(i);

✅ 输出:

复制代码
1
2
3

🧠 因为 for...of 只管数组本体值,不理 custom 属性。


🎯 面试高频场景 + 回答示例


❓ 问题一:

"请你说一下 for...infor...of 的本质区别和使用场景。"

🧠 答题模板(可套用):

markdown 复制代码
- `for...in` 用于遍历对象的可枚举属性(包括原型链)
  - 返回的是 key(属性名)
  - 不推荐用于数组,因为顺序不保证,且会遍历原型
- `for...of` 用于遍历可迭代对象(数组、字符串、Map、Set)
  - 返回的是 value(元素值)
  - 本质依赖 `[Symbol.iterator]` 协议
  - 推荐用于数组与类数组结构的遍历

❓ 问题二:

"如果要遍历对象的值,你会用哪个?"

✅ 标准回答:

css 复制代码
对象不是可迭代的,不能直接用 for...of。
可以先用 Object.entries(obj) 拿到 [key, value] 数组,再用 for...of。
javascript 复制代码
for (let [k, v] of Object.entries(obj)) {
  console.log(k, v);
}

相关推荐
小飞悟14 分钟前
那些年我们忽略的高频事件,正在拖垮你的页面
javascript·设计模式·面试
绅士玖17 分钟前
📝 深入浅出 JavaScript 拷贝:从浅拷贝到深拷贝 🚀
前端
中微子27 分钟前
闭包面试宝典:高频考点与实战解析
前端·javascript
brzhang27 分钟前
前端死在了 Python 朋友的嘴里?他用 Python 写了个交互式数据看板,着实秀了我一把,没碰一行 JavaScript
前端·后端·架构
G等你下课1 小时前
告别刷新就丢数据!localStorage 全面指南
前端·javascript
该用户已不存在1 小时前
不知道这些工具,难怪的你的Python开发那么慢丨Python 开发必备的6大工具
前端·后端·python
爱编程的喵1 小时前
JavaScript闭包实战:从类封装到防抖函数的深度解析
前端·javascript
LovelyAqaurius1 小时前
Unity URP管线着色器库攻略part1
前端
Xy9101 小时前
开发者视角:App Trace 一键拉起(Deep Linking)技术详解
java·前端·后端
lalalalalalalala1 小时前
开箱即用的 Vue3 无限平滑滚动组件
前端·vue.js