MDN 文档正在讨论完全替换原型,而不是向其添加新属性或方法(由于内部 [[Prototype]] 属性是共享的,因此将添加到共享该原型的所有对象中)。考虑这个例子:
function Person(name, age)
{
this.name = name?name:"Parent Function";
this.age = age?age:"Old as Time";
}
Person.prototype.strength = "some strength";
var parent = new Person("Ebeneezer", 42);
console.log(parent.strength); //"some strength"
//Replace `Person.prototype` with a completely new prototype object
Person.prototype = {
//setting the 'constructor' property correctly when replacing a prototype object
//is a best practice, but it will work without this too
constructor: Person
};
console.log(parent.strength); //still "some strength"
var child = new Person("Aluiscious", 12);
//This will be undefined, because the object was created after the prototype was changed
console.log(child.strength);
在上面的例子中,实例的 [[Prototype]] 属性引用了两个不同的原型对象,因为我.prototype =
在创建第二个对象之前替换了原型。
了解内部原型属性在使用相同原型创建的所有实例之间共享是很重要的。这就是为什么在您的示例中,该strength
属性被添加到两个对象中 - 两个对象的内部 [[Prototype]] 属性仍然是对同一个共享原型对象的引用。认识到原型的对象和数组属性也是共享的也很重要。例如,假设您在原型children
中添加了一个数组:Person
//Don't do this!
Person.prototype.children = [];
var parent1 = new Person("Ebeneezer", 42);
parent1.children.push(new Person("Child A"));
var parent2 = new Person("Noah", 35);
parent2.children.push(new Person("Child B"));
你可能认为这会导致 Ebenezer 有一个只包含 Child A 的数组,而 Noah 有一个只包含 Child B 的数组,但实际上父母双方现在都有一个包含 BOTH Child A 和 Child B 的数组,因为children
实际上是指属于内部 [[Prototype]] 对象的同一个数组。
这就是为什么我认为始终在构造函数中声明数据属性并且仅在原型上声明方法是最佳实践。例如:
function Person(name, age)
{
this.name = name?name:"Parent Function";
this.age = age?age:"Old as Time";
this.children = [];
}
//it's fine to declare methods on the prototype - in fact it's good, because it saves
//memory, whereas if you defined them in the constructor there would be a separate copy
//of the method for each instance
Person.prototype.addChild = function(child) {
if (!child instanceof Person) {
throw new Error("child must be a Person object");
}
//Note: in a real system you would probably also want to check that the passed child
//object isn't already in the array
this.children.push(child);
}
注意:除了原型本身之外,修改与替换的概念还适用于原型属性。如果你直接在一个对象上设置一个属性,它将被用来代替原型上的属性。因此,如果我要将上面的示例更改为:
Person.prototype.children = [];
var parent1 = new Person("Ebeneezer", 42);
parent1.children.push(new Person("Child A"));
var parent2 = new Person("Noah", 35);
parent2.children = [];
//now `parent2` has its own `children` array, and Javascript will use that
//instead of the `children` property on the prototype.
parent2.children.push(new Person("Child B"));
...然后两个父级将有单独children
的数组,但当然我提到这只是为了说明目的,您应该在构造函数中声明数组或对象属性,如我上面所示。在这个例子中,children
数组 forparent1
仍然引用children
原型上的属性,所以如果你要创建一个新的 Person 对象,那么它仍然会children
与 Ebenezer 共享:
var parent3 = new Person("Eve");
console.log(parent3.children); //array containing Child A
这篇文章也可能有助于理解这一点:http ://www.bennadel.com/blog/1566-using-super-constructors-is-critical-in-prototypal-inheritance-in-javascript.htm