这篇文章终于搞明白了 for...in 了

141 阅读3分钟

这篇文章主要讲一讲 for...in, 上篇文章介绍了 for...of 。

用法

我们先看下用法:

const person = {
  name: '张三',
  age: 30,
  job: '工程师'
};

for (const key in person) {
  console.log(key + ': ' + person[key]);
}
// 输出:
// name: 张三
// age: 30
// job: 工程师

概念

for...in 语句迭代一个对象的除了Symbol以外的所有 可枚举 字符串属性,包含从原型链上 继承 的可枚举属性。

新词汇:可枚举 ,看看这个什么意思

可枚举

可枚举属性是指那些内部的 可枚举属性 设置为true的属性。 可能第一次接触有点抽象,我们逐步展开。

JS可以使用 Object.defineProperty() 方法直接在对象上定义一个新属性,或者修改对象上的现有属性,并返回该对象。

const obj = {};

Object.defineProperty(obj, 'name', {
  value: '张三', // 属性的值
  writable: true, // 是否可写
  enumerable: true, // 是否可枚举
  configurable: true // 是否可配置
});

console.log(obj.name); // "张三"

聪明的你这时候肯定就明白了对象中的属性可以被枚举就是因为 enumerable 描述符为 true的情况下。

需要注意的是:

  • 如果直接使用 Object.defineProperty() 去定义一个属性值,但是其他的描述都不配置,默认都是false。
  • 如果我们直接通过赋值的属性或者直接初始化一个对象创建的属性,则他们的属性的标识符默认是true。

for...in 返回的顺序

for...in 遍历对象时,虽然顺序不是按照声明顺序返回的,但是遍历的顺序也是可以预测的(ES6之后):

  • 首先是所有非负整数键按值的升序遍历
  • 其他字符串按属性创建的先后顺序升序遍历
const obj = {
  2: 'two',
  1: 'one',
  b: 'b',
  abc: 'ABC',
  a: 'a'
};

for (const key in obj) {
  console.log(key); 
  // 输出顺序: "1", "2", "b", "abc", "a"
}

至此,我们已经了解完for...in了。

for...in 的数组迭代

我们知道 JS中万物皆对象,数组也是一样。数组只不过是将他的索引作为了属性,除此之外与一般对象属性完全相同。

const colors = ['red', 'green', 'blue'];

for (const index in colors) {
  console.log(index + ': ' + colors[index]);
}
// 输出:
// 0: red
// 1: green
// 2: blue

需要注意的是for...in使用的是数组的属性枚举,而for...of是使用的数组的迭代器。

这个差别就体现在了遍历稀疏数组上,for...of会访问空槽,for...in会跳过空槽,只遍历有值得属性。

// 创建一个稀疏数组
const sparseArray = [1, , 3]; // 注意中间的逗号表示空槽
sparseArray[4] = 5; // 索引2和3是空槽

console.log(sparseArray); // [1, empty, 3, empty, 5]

// 使用 for...of 遍历
console.log('for...of 遍历:');
for (const value of sparseArray) {
  console.log(value); 
}
// 输出:
// 1
// undefined (空槽)
// 3
// undefined (空槽)
// 5

// 使用 for...in 遍历
console.log('for...in 遍历:');
for (const index in sparseArray) {
  console.log(`索引 ${index}: ${sparseArray[index]}`);
}
// 输出:
// 索引 0: 1
// 索引 2: 3 (跳过了空槽1)
// 索引 4: 5 (跳过了空槽3)

仅遍历自身属性

因为for...in 是可以迭代原型链上的属性:

function Person() {
  this.name = 'Alice';
}
Person.prototype.age = 30;

const p = new Person();
for (const key in p) {
  console.log(key); // 'name', 'age'
}

但是我们一般平时的开发中,需求大多数都是遍历自身的属性,并且也是希望遍历的顺序是我们期望的,可以看看一下方法

1. 使用 Object.keys() + forEach() / for...of

const obj = { a: 1, b: 2 };

// 方案1: forEach
Object.keys(obj).forEach(key => {
  console.log(key, obj[key]);
});

// 方案2: for...of
for (const key of Object.keys(obj)) {
  console.log(key, obj[key]);
}

2. Object.entries() (ES2017+)

const obj = { a: 1, b: 2 };

// 方案1: forEach
Object.entries(obj).forEach(([key, value]) => {
  console.log(key, value);
});

// 方案2: for...of
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value);
}

新手前端,不正确的地方,期望指正!