Почему наедаются оба хомяка?
У нас есть два хомяка: шустрый (speedy
) и ленивый (lazy
); оба наследуют от общего объекта hamster
.
Когда мы кормим одного хомяка, второй тоже наедается. Почему? Как это исправить?
let hamster = {
stomach: [],
eat(food) {
this.stomach.push(food);
}
};
let speedy = {
__proto__: hamster
};
let lazy = {
__proto__: hamster
};
// Этот хомяк нашёл еду
speedy.eat("apple");
alert( speedy.stomach ); // apple
// У этого хомяка тоже есть еда. Почему? Исправьте
alert( lazy.stomach ); // apple
Давайте внимательно посмотрим, что происходит при вызове speedy.eat("apple")
.
-
Сначала в прототипе (
=hamster
) находится методspeedy.eat
, а затем он выполняется сthis=speedy
(объект перед точкой). -
Затем в
this.stomach.push()
нужно найти свойствоstomach
и вызвать для негоpush
. Движок ищетstomach
вthis
(=speedy
), но ничего не находит. -
Он идёт по цепочке прототипов и находит
stomach
вhamster
. -
И вызывает для него
push
, добавляя еду в живот прототипа.
Получается, что у хомяков один живот на двоих!
И при lazy.stomach.push(...)
и при speedy.stomach.push()
, свойство stomach
берётся из прототипа (так как его нет в самом объекте), затем в него добавляются данные.
Обратите внимание, что этого не происходит при простом присваивании this.stomach=
:
let hamster = {
stomach: [],
eat(food) {
// присвоение значения this.stomach вместо вызова this.stomach.push
this.stomach = [food];
}
};
let speedy = {
__proto__: hamster
};
let lazy = {
__proto__: hamster
};
// Шустрый хомяк нашёл еду
speedy.eat("apple");
alert( speedy.stomach ); // apple
// Живот ленивого хомяка пуст
alert( lazy.stomach ); // <ничего>
Теперь всё работает правильно, потому что this.stomach=
не ищет свойство stomach
. Значение записывается непосредственно в объект this
.
Также мы можем полностью избежать проблемы, если у каждого хомяка будет собственный живот:
let hamster = {
stomach: [],
eat(food) {
this.stomach.push(food);
}
};
let speedy = {
__proto__: hamster,
stomach: []
};
let lazy = {
__proto__: hamster,
stomach: []
};
// Шустрый хомяк нашёл еду
speedy.eat("apple");
alert( speedy.stomach ); // apple
// Живот ленивого хомяка пуст
alert( lazy.stomach ); // <ничего>
Все свойства, описывающие состояние объекта (как свойство stomach
в примере выше), рекомендуется записывать в сам этот объект. Это позволяет избежать подобных проблем.