JS-一文彻底搞懂 for...in 和 for...of 的区别

64 阅读3分钟

前言

在 JavaScript 中,遍历数据是我们每天都在做的事情。但是,你真的用对了吗?为什么有时候遍历数组会出现奇怪的属性?为什么对象不能用 for...of?本文带你从底层机制区分这两个“双胞胎”。

一、 核心区别速览(一张表看懂)

简单来说:for...in 是为遍历对象属性而构建的,不建议用于数组;for...of 是为遍历迭代器(Iterator)对象而构建的。

特性for...infor...of (ES6)
遍历目标对象 (Object)可迭代对象 (Array, Map, Set, String...)
获取内容键名 (Key)键值 (Value)
推荐场景调试、查看对象属性遍历数组、类数组、Map/Set

二、 for...in:对象的“老管家”

for...in 循环主要用于遍历对象的可枚举属性(enumerable properties)。

1. 遍历数组的“坑”

虽然它能遍历数组,但强烈不推荐

  • 索引是字符串:输出的 0, 1 是字符串类型,不是数字,进行计算容易出错。
  • 原型链污染:如果有人在 Array.prototype 上加了方法,for...in 会把这个方法也遍历出来!
Array.prototype.sayHello = function() { 
    console.log("Hello"); 
}
const arr = ['a', 'b'];

for (let key in arr) {
    console.log(key); 
    // 输出: "0", "1", "sayHello" 
    // (看!多了一个不想要的东西)
}

2. 正确用法:遍历对象

const obj = { name: 'ouyang', age: 12 };
for (let key in obj) {
    // 建议加上 hasOwnProperty 过滤原型链属性
    if (obj.hasOwnProperty(key)) {
        console.log(key, obj[key]); // name ouyang, age 12
    }
}

三、 for...of:数组的“新宠儿”

for...of 是 ES6 引入的新语法,它专门用于 Iterator(迭代器) 接口。

1. 强大的兼容性

只要一个对象部署了 [Symbol.iterator] 属性,它就能用 for...of 遍历。这意味着它不仅支持数组,还支持:

  • 字符串 (String)
  • Map 和 Set
  • arguments 对象
  • NodeList (DOM 列表)
// 遍历字符串
for (let char of "Hello") {
    console.log(char); // "H", "e", "l", "l", "o"
}

2. 为什么遍历对象会报错?

const obj = { name: 'ouyang', age: 12 };
// for (let item of obj) {} 
// ❌ Uncaught TypeError: obj is not iterable

原因:普通对象(Object)默认没有部署 Iterator 接口。

解决:结合 Object.keys() 使用。

for (let key of Object.keys(obj)) {
    console.log(key + ': ' + obj[key]);
}

四、 面试模拟题

Q1:为什么不建议使用 for...in 遍历数组?

参考回答:

  1. 原型链干扰for...in 会遍历数组原型链上所有可枚举的属性,如果原型被扩展(如添加了自定义方法),这些属性也会被遍历出来,导致意外错误。
  2. 类型问题:它返回的索引是 String 类型(如 "0"),而不是 Number,这在涉及索引计算时容易产生 Bug(例如 "1" + "2" = "12")。
  3. 语义不符:它的设计初衷是遍历对象的键,而不是数组的项。

Q2:for...of 可以遍历对象吗?如果非要遍历怎么办?

参考回答:

直接使用 for...of 遍历普通对象会报错,因为对象没有 Symbol.iterator 接口。

如果非要用,有两种方法:

  1. 间接遍历:配合 Object.keys(obj)Object.values(obj)Object.entries(obj) 生成数组后遍历。
  2. 自定义迭代器:给对象手动添加 [Symbol.iterator] 属性(属于高阶玩法)。

Q3:forEachfor...infor...of 有什么区别?

参考回答:

  • forEach:数组的高阶方法,无法使用 breakreturn 跳出循环。
  • for...in:遍历键名,会遍历原型链,适合对象。
  • for...of:遍历键值,支持 break,适合数组和所有可迭代对象。