for in 和for of

212 阅读3分钟

在 JavaScript 中,for...infor...of 是两种常用于遍历的语法,但它们在用途、机制和适用场景上有显著差异。以下从核心区别到实践场景的详细解析:

🔍 一、核心区别

特性for...infor...of
遍历目标对象的可枚举属性(包括原型链)可迭代对象的值(Array, Map, Set等)
输出内容属性名(key/index)属性值(value)
适用数据类型普通对象、数组(不推荐)数组、字符串、Map、Set 等可迭代对象
原型链属性默认遍历,需用 hasOwnProperty 过滤不遍历原型链属性
Symbol 属性无法遍历 Symbol 键可遍历 Symbol 键(若对象实现了迭代器)

示例对比

const arr = [3, 5, 7]; arr.foo = "自定义属性";
// for...in:输出索引及自定义属性 
for (let key in arr) { console.log(key); } // "0", "1", "2", "foo" 
// for...of:仅输出值 
for (let value of arr) { console.log(value); } // 3, 5, 7


⚙️ 二、工作机制详解

  1. for...in 的遍历机制

    • 遍历对象自身及原型链上所有可枚举属性(非 Symbol 类型)。
    • 顺序不固定:数字键优先升序,其他键按添加顺序,但不同引擎可能不一致 [[2][38]]。
    • 需用 obj.hasOwnProperty(key) 过滤原型链属性:
for (let key in obj) { 
if (obj.hasOwnProperty(key)) { console.log(key, obj[key]); // 仅输出自身属性 
      }
}

for...of 的依赖条件

  • 要求对象实现 Symbol.iterator 接口(数组、Map等内置支持)。
  • 普通对象不可直接使用,需通过以下方式转换
// 方法1:搭配 Object.keys() 
for (let key of Object.keys(obj)) { 
console.log(obj[key]); 
} 
// 方法2:自定义迭代器 
obj[Symbol.iterator] = function* () { 
for (let key in this) { yield this[key]; }
};

⚠️ 三、关键注意事项

  1. 遍历顺序问题

    • for...in 对数组遍历时,顺序可能因引擎差异或数字键排序而混乱,不适合需要顺序的场景 [[1][38]]。
    • for...of 严格按可迭代对象的内部顺序输出(如数组按索引顺序)。
  2. 性能差异

// 性能测试结果(大数据量下):
for: 21ms // 传统 for 循环最快 
forEach: 319ms // 数组方法 
for...of: 463ms // 迭代器机制 
for...in: 18142ms // 原型链检查导致极慢
  1. 建议:遍历大数组时优先使用 forfor...of

  2. 中断控制

    • for...in 和 for...of 均支持 breakcontinue 控制流程。
    • forEach 等数组方法无法中断

🎯 四、适用场景总结

场景推荐语法原因
遍历对象自身属性for...in + hasOwnProperty精准控制属性来源,避免原型链污染
遍历数组值for...of简洁安全,不涉及非元素属性(如自定义属性)
中断循环的操作for...of 或 for支持 break/continue,而 forEach 不支持  38
类数组结构(NodeList等)for...of直接遍历值,无需下标转换  
遍历 Map/Setfor...of直接获取键值对(如 [key, value]

💎 五、总结建议

  • for...in 适用: 对象属性遍历(需过滤原型链),避免用于数组

  • for...of 适用: 数组、字符串、Map/Set 等可迭代对象的值遍历,不支持普通对象(需转换)。

  • 性能敏感场景: 大数据量优先选传统 for 循环,其次是 for...of禁用 for...in 遍历数组   。

  • 扩展参考: 遍历对象属性可用 Object.keys() + for...of 组合,兼顾安全性与现代语法 。

通过理解上述机制与场景差异,可避免常见陷阱(如原型链污染、顺序混乱),写出高效可靠的遍历逻辑。