欢迎光临
我们一直在努力

for和of的用法区别

问答中心分类: 其他for和of的用法区别
1 回复
0
长恨春归晚 回复于 2025-11-27 之前

for...infor...of 这两个东西在 JavaScript 里都用来做循环,但它们用起来完全不是一回事。要是搞不清楚,代码很容易出问题。

咱们先说 for...in。这个循环是用来遍历一个对象的所有可枚举属性的,简单说就是拿出对象里所有的键(key)。

看这个例子,我们有一个普通的对象 person

“`javascript
const person = {
name: ‘老王’,
age: 30,
job: ‘程序员’
};

for (const key in person) {
console.log(key);
}
“`

运行这段代码,你会看到控制台输出了 nameagejob。这就是 for...in 的作用,它把 person 对象里的每个键都拿出来,让你能用它们做点什么。如果你想同时拿到键和对应的值,可以这么写:

javascript
for (const key in person) {
console.log(key + ': ' + person[key]);
}

这样,输出就变成了:
name: 老王
age: 30
job: 程序员

看起来挺好用,对吧?但问题就出在,很多人会误以为它也能很好地处理数组。理论上,数组也是对象,它的键就是索引(0, 1, 2…),所以 for...in 确实能用在数组上。

比如下面这个数组:

“`javascript
const fruits = [‘苹果’, ‘香蕉’, ‘橘子’];

for (const index in fruits) {
console.log(index);
}
“`

控制台会输出 012。这些都是字符串,不是数字。这是第一个要注意的地方,for...in 拿到的键永远是字符串。如果你想用这个索引去做数学计算,还得先把它转成数字。

但这还不是最大的问题。for...in 最大的坑在于它会遍历原型链上的属性。JavaScript 的对象都有一个叫“原型”的东西,对象可以从原型上继承属性和方法。如果有人不小心修改了数组的原型,for...in 就会把那些不属于数组本身的属性也给遍历出来。

举个例子,假设有人在别的地方给所有数组加了一个新的方法:

“`javascript
Array.prototype.sayHello = function() {
console.log(‘你好’);
};

const fruits = [‘苹果’, ‘香蕉’, ‘橘子’];

for (const index in fruits) {
console.log(index);
}
“`

这时候再运行,输出结果就不只是 012 了,还会多一个 sayHello。这就完全不是我们想要的结果。在实际开发中,你可能根本不知道哪个库或者哪个同事动了原型,这个问题会变得很难排查。

另外,for...in 循环也不能保证遍历的顺序。虽然大部分现代浏览器会按照 0, 1, 2 的顺序来,但根据 JavaScript 的规范,这个顺序是不确定的。所以,如果你想按顺序处理数组里的元素,用 for...in 是一个很冒险的行为。

所以结论很简单:除非你非常确定自己要干什么,比如检查一个普通对象的内部构造,否则就别用 for...in 来遍历数组。

接下来说 for...of。这是在 ES6(ECMAScript 2015)里新加的循环方式,它的出现很大程度上就是为了解决 for...in 的那些问题。

for...of 是专门用来遍历可迭代对象(iterable objects)的值的。什么是可迭代对象?简单理解,就是那些内部实现了特定接口,可以让你按顺序一个一个拿出其中元素的对象。常见的可迭代对象包括数组、字符串、Map、Set 等。

我们用 for...of 来试试刚才那个水果数组:

“`javascript
const fruits = [‘苹果’, ‘香蕉’, ‘橘子’];

for (const fruit of fruits) {
console.log(fruit);
}
“`

这次,控制台输出的是 苹果香蕉橘子。看到了吗?它直接拿出了数组里的值,而不是索引。这才是我们通常想要的结果。

for...of 有几个非常好的优点:
1. 直接获取值:它拿到的是元素的值,不是键或索引,代码写起来更直接。
2. 不会遍历原型链:它只关心数据本身,就算你修改了 Array.prototypefor...of 也不会去管那些新增的属性或方法。
3. 保证顺序:对于数组、字符串这类有序的集合,for...of 会严格按照元素的顺序进行遍历。
4. 适用范围广:除了数组,它还能用在很多其他数据结构上。

比如字符串:

“`javascript
const message = ‘你好’;

for (const char of message) {
console.log(char);
}
“`

输出会是:

它把字符串里的每个字符都按顺序拿出来了。

但是,for...of 不能用在普通的对象上,因为普通的对象默认不是可迭代的。如果你非要这么做,程序会直接报错。

“`javascript
const person = {
name: ‘老王’,
age: 30
};

// 下面这行代码会报错: person is not iterable
for (const value of person) {
console.log(value);
}
“`

这一点也正好和 for...in 形成了互补。for...in 的主场是普通对象,而 for...of 的主场是数组、字符串这类可迭代的数据集合。

现在我们来总结一下它们的核心区别:

  • 遍历的东西不同

    • for...in 遍历的是对象的(key),而且这些键是字符串。
    • for...of 遍历的是可迭代对象的(value)。
  • 适用对象不同

    • for...in 可以用在任何对象上,但主要设计用来遍历普通对象的属性。
    • for...of 只能用在可迭代对象上,比如数组、字符串、Map、Set 等。
  • 安全性和可靠性

    • for...in 会遍历原型链上的属性,并且不保证顺序,用在数组上风险很高。
    • for...of 不会受原型链影响,能保证遍历顺序,是遍历数组等有序集合的现代、安全的方式。

那么,在实际工作中到底该怎么选?

规则很简单:

  1. 如果你要遍历一个数组或者字符串,用 for...of 这是最直接、最安全的选择。它能让你拿到每个元素的值,并且不用担心顺序和原型链污染的问题。

  2. 如果你要遍历一个普通对象的属性,用 for...in 这是它的本职工作。不过,为了避免遍历到原型链上的属性,最好加上一个检查。

    “`javascript
    const person = {
    name: ‘老王’,
    age: 30
    };

    for (const key in person) {
    if (Object.prototype.hasOwnProperty.call(person, key)) {
    console.log(key + ‘: ‘ + person[key]);
    }
    }
    “`

    hasOwnProperty.call() 这段代码的作用就是检查这个属性是不是对象自己的,而不是从原型上继承来的。虽然写起来麻烦一点,但能让代码更健壮。

不过,现在处理对象还有更好的方法。比如,如果你只想拿到对象所有的键,可以用 Object.keys();如果只想拿到所有的值,可以用 Object.values();如果想同时拿到键和值,可以用 Object.entries()。这些方法都会返回一个数组,然后你就可以用 for...of 来处理了。

“`javascript
const person = {
name: ‘老王’,
age: 30
};

// 遍历键
for (const key of Object.keys(person)) {
console.log(key);
}

// 遍历值
for (const value of Object.values(person)) {
console.log(value);
}

// 遍历键和值
for (const [key, value] of Object.entries(person)) {
console.log(key + ‘: ‘ + value);
}
“`

用这种方式来处理对象,代码不仅更清晰,也避免了 for...in 的那些潜在问题。所以,现在很多人即使在处理普通对象时,也越来越少直接用 for...in 了。

总的来说,for...of 是现代 JavaScript 中处理循环遍历的主要方式,尤其是在和数组、字符串打交道时。而 for...in 则更像一个专门用来检查对象内部结构的工具,使用时需要格外小心。理解了这一点,就能在写代码时做出正确的选择。

 

登录

找回密码

注册