Документы 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"));
Вы могли бы ожидать, что это приведет к тому, что у Эбенизера будет массив, содержащий только дочерний элемент A, а у Ноя будет массив, содержащий только дочерний элемент B, но на самом деле оба родителя теперь будут иметь массив, содержащий ОБОИХ дочерних элементов A и дочерний элемент 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
для parent1
по-прежнему ссылается на свойство children
в прототипе, поэтому, если вы создадите новый объект Person, он все равно разделит children
с Ebeneezer:
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
person
Matt Browne
schedule
30.08.2014