1. 迭代顺序:
for-in 循环并不是按照对象属性在对象中的顺序迭代的。我们在使用 for-in 循环迭代对象时,不能保证它们的顺序。
顺序大概是:
- 先整数形式的字符串键升序
- 后其他的键(包含小数形式的键)按创建的顺序
- 再按原型链向上遍历
不要依赖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迭代顺序是确定的,按照属性被插入的顺序
- 迭代目标
for in迭代键,for of 迭代值。for in 也会迭代数组或字符串的索引
- 原型链
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不包括原型链
- 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...in | for...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()
- 数字索引属性(数组索引属性)优先,按升序排列
- 字符串属性(包括符号属性)按照它们被添加到对象的顺序排列
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"