使用 JavaScript 中的实例更新原型

Updating a prototype using an instance in JavaScript

我有这个构造函数Person:

function Person(name) {
  this.name = name;
}

我在其原型上添加了一个数组 favorites 以便 Person 的所有实例都可以指向同一个数组:

Person.prototype.favorites = [];
var person1 = new Person("KimK");
var person2 = new Person("KhloeK");

person1.favorites = ["coke"]; 
person2.favorites = ["pepsi"];

console.log(person1.favorites); //["coke"]
console.log(person2.favorites); //["pepsi"]

我希望两个日志语句都打印 ["pepsi"],因为 person1person2 都指向原型上的同一个基本数组 favorites,并且 person2 最后修改为 ["pepsi"],我预计两个输出都是 ["pepsi"]。我错过了什么吗?

当您将 ["coke"] 分配给 person1.favorites 时,您修改的是 person1 的 属性,而不是 Person 原型。您可以通过 console.log(Person.prototype.favorites) 轻松检查它——它仍然包含空数组。如果要更改所有实例的值,唯一的方法是直接修改原型,即:

Person.prototype.favorites = ["coke"];

但是,如果您没有设置favorites 属性到Person个实例,您可以通过修改person1.favorites来修改原型。例如:

function Person(name) {
  this.name = name;
}
Person.prototype.favorites = [];

var person1 = new Person("KimK");
person1.favorites.push("coke", "pepsi")

console.log(person1.favorites); // ["coke", "pepsi"]

var person2 = new Person("KhloeK");
console.log(person2.favorites); // ["coke", "pepsi"]

console.log(Person.prototype.favorites); // ["coke", "pepsi"]

这会为 person1 的收藏夹分配一个新数组 属性。它不会修改其原型上现有的:

person1.favorites = ["coke"];

您可以使用 push 附加到数组,也可以通过将其长度设置为 0 来清空数组。例如:

person1.favorites.push( 'coke' );
person2.favorites.length = 0;        
person2.favorites.push( 'coke' );

读取对象属性可能涉及搜索继承链。

读取 javascript 对象 属性 首先搜索对象的本地 属性 名称。如果没有找到本地或 "own" property,则通过依次查看继承链中的每个原型对象继续搜索,直到找到 属性 名称或检查了继承链中的所有对象。如果找到该名称,则从找到它的对象中获取其值。如果找不到读取 returns undefined 作为命名的 属性 值。

写入对象属性总是*在本地写入 属性 值

写入命名值 属性 的值会创建或更新被写入对象持有的本地 属性 值。如果它之前是通过继承链继承的,它将停止被继承:优先读取 returns 本地值。请注意,继承它的对象的继承 属性 值保持不变。

所以

person1.favorites = ["coke"]; 
person2.favorites = ["pepsi"];

在写入 ["coke"] 之前创建一个 person1 的新本地 favorites 属性,以及一个新的本地 favorites 属性 person2 写入 ["pepsi"] 之前。

Person.prototype、[] 继承的先前 favorites 属性 值在进程中被隐藏。


*排除在 ES5 中定义的 Getters 和 Setters

Setter and getter 函数将 属性 名称与对象相关联。 属性 以这种方式设置的名称缺少内部 [[value]] 槽,但可以作为 getter 和 setter 函数继承。特别是写入对象确实会在继承链中搜索 setter,如果同名的本地 属性 尚不存在。

虽然 getters 和 setters 被调用的对象作为它们的 this 值被读取或写入,但它们存储和检索值的位置和方式无法概括,因为它取决于它们是如何写的。