for in 和 for of的区别

125 阅读3分钟

1. 迭代顺序:

for-in 循环并不是按照对象属性在对象中的顺序迭代的。我们在使用 for-in 循环迭代对象时,不能保证它们的顺序。

顺序大概是:

  1. 先整数形式的字符串键升序
  2. 后其他的键(包含小数形式的键)按创建的顺序
  3. 再按原型链向上遍历

不要依赖for in遍历顺序,用.keys()/.values(),因为他们不会遍历原型链

const obj = { b: 2, a: 1, '10.1': 100, '2': 20 };
// "2" "b" "a" "10.1"
for (const key in obj) {
    console.log(key);
}

for...of迭代顺序是确定的,按照属性被插入的顺序

  1. 迭代目标

for in迭代键,for of 迭代值。for in 也会迭代数组或字符串的索引

  1. 原型链

for in 会遍历对象及其原型链上的所有可枚举属性。for of 不会

// 定义一个构造函数 Person
function Person(name) {
  this.name = name; // 实例属性
}

// 在 Person 的原型上添加一个方法
Person.prototype.greet = function() {
  console.log("Hello, my name is " + this.name);
};

// 在 Person 的原型上添加一个属性
Person.prototype.age = 30;

// 创建一个 Person 实例
const person = new Person("Alice");

console.log("for...in 循环遍历 person 对象:");
for (let key in person) {
  console.log(key);
}

// 输出结果:
// name
// age
// greet

由于 for-in 循环会遍历对象的原型链,因此可能会迭代到不是自身属性的属性。为了避免这种情况,我们可以使用 hasOwnProperty 方法来检查属性是否为对象自身的属性。

const person = {
  name: 'John',
  age: 30,
  gender: 'male'
};
 
for (const key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key, person[key]);
  }
}

for of不包括原型链

  1. for of 可以用来遍历 map 和 set, for in 不能,因为map和Set不是一个普通对象,它没有可枚举的属性名
const mySet = new Set([1, 2, 3]); // 使用 for...of 
for (const value of mySet) { console.log(value); } // 输出:1, 2, 3

const myMap = new Map(); 
myMap.set('apple', 1); 
myMap.set('banana', 2); // 使用 for...of (推荐) 
for (const [key, value] of myMap) { console.log(`${key} => ${value}`); } 
// 输出: // apple => 1 // banana => 2

总结

特性for...infor...of
遍历对象遍历可枚举属性名 (key)遍历可迭代对象的值 (value)
适用范围主要用于遍历普通对象的属性适用于可迭代对象 (数组、字符串、Map、Set等)
数组遍历不推荐,会遍历索引 (字符串) 和自定义属性推荐,直接获取元素值,语义清晰
原型链会遍历原型链上的可枚举属性 (通常需hasOwnProperty过滤)不会遍历原型链上的属性

for in 和 Object.keys() 区别?

  • for...in 循环会遍历对象自身的可枚举属性以及原型链上可枚举的属性
  • Object.keys()Object.values()Object.entries() 只会遍历对象自身的可枚举属性

补充:Object.keys, Object.values, Object.entries

Object.keys()

  1. 数字索引属性(数组索引属性)优先,按升序排列
  2. 字符串属性(包括符号属性)按照它们被添加到对象的顺序排列
const obj = {
  100: 'a',
  2: 'b',
  7: 'c',
  foo: 'd',
  bar: 'e'
};

obj.baz = 'f'; // 添加 baz 属性

console.log(Object.keys(obj)); // 输出: ["2", "7", "100", "foo", "bar", "baz"]

Object.values()

顺序和.keys()一样,按照键的顺序

```js
const obj = {
  100: 'a',
  2: 'b',
  7: 'c',
  foo: 'd',
  bar: 'e'
};

obj.baz = 'f'; // 添加 baz 属性

console.log(Object.values(obj)); // 输出: ["b", "c", "a", "d", "e", "f"]
```

Object.entries()

1、返回一个键值对数组(不含继承的)

var obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj));
// [
// ["foo","bar"],
// ["baz",42]
// ]

2、如果原对象的属性名是一个 Symbol 值,该属性会被省略

console.log(Object.entries({ [Symbol()]: 123, foo: 'abc' }));  // [ [ 'foo', 'abc' ] ]

3、遍历对象的属性

let obj = {
    one : 1,
    two : 2,
}
for(let [k , v] of Object.entries(obj)){
  console.log(k)
  console.log(v)
}

// "one"
// 1
// "two"
// 2
for(let [k , v] of Object.entries(obj)){
  console.log(v)
  console.log(k)
}
// 1
// "one"
// 2
// "two"