今天咱们来聊聊 JavaScript 里两个看似相似但完全不同的循环语句——for...in
和 for...of
。
你是不是也曾经纠结过:
for...in
和for...of
到底该用哪个?- 为什么有时候遍历数组会得到奇怪的结果?
- 遍历对象时,哪种方式更安全?
1. 一句话总结区别
for...in
→ 遍历对象的可枚举属性(包括原型链)for...of
→ 遍历可迭代对象的值(Array、Map、Set等)
简单来说:
for...in
适合对象(但要注意原型链问题)for...of
适合数组、字符串等可迭代结构
2. for...in
:遍历对象的“家底”
for...in
会遍历对象的所有可枚举属性,包括继承自原型链的属性。
举个栗子:遍历对象
const user = { name: '我', age: 25, role: 'developer' };
for (const key in user) {
console.log(key, user[key]);
}
// 输出:
// name 我
// age 25
// role developer
看起来没问题?但如果原型链上有属性呢?
Object.prototype.customProp = '来自原型链';
for (const key in user) {
console.log(key);
}
// 输出:
// name
// age
// role
// customProp (意外多了一个属性!)
解决方案:用 hasOwnProperty
过滤
for (const key in user) {
if (user.hasOwnProperty(key)) { // 只遍历自己的属性
console.log(key);
}
}
// 输出:
// name
// age
// role
⚠️ 注意:for...in
不保证顺序!
- 对象的属性遍历顺序不固定(尤其是数字键时)
- 如果需要顺序,改用
Object.keys()
+for...of
3. for...of
:专治可迭代对象
for...of
专门用于遍历可迭代对象(Iterable) ,比如:
✅ Array
✅ String
✅ Map / Set
✅ NodeList(DOM 元素集合)
✅ arguments 对象
举个栗子:遍历数组
const skills = ['JS', 'CSS', 'React'];
for (const skill of skills) {
console.log(skill);
}
// 输出:
// JS
// CSS
// React
比 for...in
更安全,不会遍历到奇怪的东西!
再举个栗子:遍历字符串
const name = '我';
for (const char of name) {
console.log(char);
}
// 输出:
// 我
for...of
不能直接遍历普通对象!
const user = { name: '我' };
for (const val of user) { // ❌ 报错!
console.log(val);
}
// TypeError: user is not iterable
解决方案:用 Object.values()
/ Object.entries()
for (const val of Object.values(user)) {
console.log(val); // 输出:我
}
4. 对比总结
特性 | for...in | for...of |
---|---|---|
适用对象 | 普通对象(会遍历原型链) | 可迭代对象(Array、Map、Set等) |
返回值 | 键名(key) | 值(value) |
顺序保证 | ❌ 不保证顺序(尤其数字键) | ✅ 保证顺序 |
原型链问题 | 可能遍历到继承属性(需过滤) | 不会遍历原型链 |
适用场景 | 遍历对象属性 | 遍历数组、字符串等 |
5. 最佳实践
✅ 用 for...in
时:
- 一定要加
hasOwnProperty
检查,避免原型链污染 - 不要用于数组(顺序不可控,可能遍历到非数字键)
✅ 用 for...of
时:
- 优先用于数组、字符串、Map、Set
- 普通对象先用
Object.keys()
/Object.values()
转换
6. 终极选择指南
场景 | 推荐方式 |
---|---|
遍历对象属性 | for...in + hasOwnProperty |
遍历数组值 | for...of |
遍历字符串字符 | for...of |
遍历Map/Set | for...of |
需要索引的数组遍历 | for 循环 或 forEach |
7. 总结
for...in
→ 遍历对象属性(小心原型链)for...of
→ 遍历可迭代对象的值(数组、字符串等)- 普通对象想用
for...of
? 先用Object.keys()
/Object.values()
转换