首页 > 解决方案 > 为什么实例化的对象即使在原型被重新分配给空对象后仍然具有原型的属性?

问题描述

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

Rabbit.prototype = {};

alert( rabbit.eats ); // ?
为什么rabbit.eat在我们将其引用即构造函数原型Rabbit设置为空时显示为真?

据我所知,当编译器读取语句 (rabbit.eat) 时,它首先检查对象是否存在该属性,然后它会查看rabbit.__proto__属性的 Rabbit.prototype。但是在这里我们分配 Rabbit.prototype = {} 那么为什么对象 rabbit 仍然具有 eat 属性?

标签: javascript

解决方案


实例化 Rabbit 的原型 ,rabbit仍然原始Rabbit.prototype对象,它具有eats: true. 重新分配原型并不意味着解释器会遍历每个实例化的对象并更改原型。

如果要更改现有对象的原型,可以使用setPrototypeOf,如下所示:

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};
let rabbit = new Rabbit();
Rabbit.prototype = {};
Object.setPrototypeOf(rabbit, Rabbit.prototype);
alert(rabbit.eats); // ?

但:

警告:根据现代 JavaScript 引擎优化属性访问的本质,在每个浏览器和 JavaScript 引擎中,更改对象的 [[Prototype]] 是一项非常缓慢的操作。更改继承对性能的影响是微妙而广泛的,并且不仅限于在 Object.setPrototypeOf(...) 语句中花费的时间,还可以扩展到可以访问任何具有 [[Prototype ]] 已更改。如果您关心性能,则应避免设置对象的 [[Prototype]]。相反,使用 Object.create() 创建一个具有所需 [[Prototype]] 的新对象。

或者,您可以将原型变异为空对象,而不是重新分配原型:

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};
let rabbit = new Rabbit();

// `for..in` will iterate over all properties, including inherited ones
// if you don't want that, iterate over Object.keys instead
for (const key in Rabbit.prototype) {
  delete Rabbit.prototype[key];
}

console.log(rabbit.eats);

只要您不重新分配类的原型(这会创建一个单独的未链接对象),对的原始原型的其他突变rabbit也会改变您可以在rabbit上访问的内容:

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};
let rabbit = new Rabbit();
// mutation, not reassignment
Rabbit.prototype.eats = 'foo';

console.log(rabbit.eats); // ?


推荐阅读