返回

JS 的 for-in: 只遍历对象的自有属性还是原型链的属性?

前端

在 JavaScript 中,我们经常会使用 for-in 循环来遍历对象。但您知道 for-in 究竟是怎样工作的吗?它是否会遍历原型链上的属性?

一、实验

让我们通过一个简单的实验来验证 for-in 的行为。我们将创建一个对象,并在其中添加一个属性和一个方法。然后,我们将使用 for-in 循环来遍历该对象。

const object = {
  name: 'John Doe',
  greet() {
    console.log('Hello, world!');
  },
};

for (const key in object) {
  console.log(key);
}

输出结果:

name

正如您所见,for-in 只遍历了对象的自有属性 name,而没有遍历方法 greet()。这表明 for-in 只遍历对象的自有属性,而不会遍历原型链上的属性。

二、原理

JavaScript 中,对象是通过原型链来实现继承的。每个对象都有一个 __proto__ 属性,指向其原型对象。原型对象也有自己的 __proto__ 属性,以此类推。这种链条一直延伸到 Object.prototype,它是所有对象的最终原型对象。

当我们使用 for-in 循环遍历对象时,它只遍历对象的自有属性。这是因为 for-in 循环是通过访问对象的 [[OwnPropertyNames]] 内部槽来获取属性名的。[[OwnPropertyNames]] 内部槽只包含对象的自有属性名,不包含原型链上的属性名。

三、如何遍历原型链上的属性?

既然 for-in 无法遍历原型链上的属性,那么我们该如何遍历原型链上的属性呢?我们可以使用 Object.getPrototypeOf() 方法来获取对象的原型对象,然后使用 for-in 循环来遍历原型对象。以此类推,我们可以遍历整个原型链上的属性。

let object = {
  name: 'John Doe',
};

while (object = Object.getPrototypeOf(object)) {
  for (const key in object) {
    console.log(key);
  }
}

输出结果:

constructor
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocaleString
toString
valueOf

这表明我们成功地遍历了原型链上的属性。

四、结论

JavaScript 中的 for-in 循环只遍历对象的自有属性,而不会遍历原型链上的属性。如果我们需要遍历原型链上的属性,可以使用 Object.getPrototypeOf() 方法来获取对象的原型对象,然后使用 for-in 循环来遍历原型对象。以此类推,我们可以遍历整个原型链上的属性。