Когда у дочернего объекта есть свойство, унаследованное от объекта-прототипа, в действительности происходит то, что у дочернего объекта есть ссылка на прототип, который содержит это свойство. У ребенка нет его собственной копии. Таким образом, оба ребенка используют один и тот же массив & mdash; один на (один) Parent
объекте-прототипе, который вы присвоили Child.prototype
.
Сначала несколько картинок, затем больше текста. : -)
new Parent()
дает вам это:
+-----------------+
| Parent instance |
+-----------------+
| list = [] |
+-----------------+
... которую вы затем присваиваете Child.prototype
.
Тогда new Child()
дает вам это:
+------------------+
| Child instance 1 |
+------------------+ +-----------------+
| (prototype) |------->| Parent instance |
+------------------+ +-----------------+
| list = [] |
+-----------------+
Выполнение new Child()
снова дает вам еще один:
+------------------+ +------------------+
| Child instance 1 | | Child instance 2 |
+------------------+ +------------------+
| (prototype) |---+ | (prototype) |---+
+------------------+ | +------------------+ |
| |
| | +-----------------+
+-------------------------+---->| Parent instance |
+-----------------+
| list = [] |
+-----------------+
Итак, как вы можете видеть, все Child
экземпляры совместно используют один и тот же Parent
экземпляр (их прототип), на котором есть массив.
Когда вы говорите:
var obj = new Child();
obj.list.push("foo");
... вот что делает движок JavaScript, когда видит obj.list
:
- Проверяет, имеет ли
obj
свое собственное свойство, называемое list
. Если нет, то:
- Проверяет, имеет ли прототип
obj
свое собственное свойство, называемое list
. Если нет, то:
- Проверяет, имеет ли прототип прототипа
obj
свое собственное свойство, называемое list
.
- Etc. пока у нас не закончатся прототипы.
В вашем случае, поскольку obj
не имеет своего собственного list
свойства, движок ищет свой прототип (экземпляр Parent
, который вы присвоили Child.prototype
), который в этот случай имеет свойство. Так что этот используется. Он не копируется ни ребенку, ни чему-либо другому, он используется . И, конечно же, поскольку это массив, добавление чего-либо в массив фактически выталкивает его в массив.
Если бы вы присваивали что-то obj.list
, тогда obj
получило бы свое собственное list
свойство, разрывая цепочку. Таким образом, включение this.list = [];
в Child
даст каждому ребенку свой список.
Вы увидите это всякий раз, когда на прототипе есть ссылка на объект , где объект - это тип, который можно изменить («изменяемый» объект). Массивы, даты, обычные объекты ({}
), RegExps и т. Д., Все они имеют состояние, и все они могут быть изменены, так что вы увидите это с ними. (String
экземпляры являются неизменяемыми, поэтому, несмотря на то, что происходит то же самое, вы не видите никакого эффекта, потому что строка не может быть изменена.)
С примитивами, хотя вы наследуете их, вы не можете изменить их состояние (вы можете только заменить их новым примитивом с другим состоянием), так что вы не увидите этот же эффект. Таким образом, если obj
наследует свойство foo
от его прототипа, а foo
равно 42
, alert(obj.foo)
получит значение из прототипа и покажет "42". Единственный способ изменить foo
- это сказать obj.foo = 67
или аналогичный & mdash; который дает obj
его собственную копию foo
, отличную от копии прототипа. (Это верно, даже если вы используете такие вещи, как ++
и --
, например, ++obj.foo
; это действительно оценивается как obj.foo = obj.foo + 1
).