Вы — руководитель команды, которая разрабатывает игру, хомяковую ферму. Один из программистов получил задание создать псевадокласс хомяк "Hamster".
Объекты-хомяки должны иметь массив food для хранения еды и метод found, который добавляет к нему.
Ниже — его решение. При создании двух хомяков, если поел один — почему-то сытым становится и второй тоже.
В чём дело? Как поправить?
function Hamster() { }
copy(Hamster.prototype, {
food: [],
found: function(something) {
this.food.push(something)
}
});
// Создаём двух хомяков и кормим первого
speedy = new Hamster();
lazy = new Hamster();
speedy.found("яблоко");
speedy.found("орех");
alert(speedy.food.length); // 2
alert(lazy.food.length); // 2 (!??)
/* вспомогательная функция copy(dst, src1, src2...) */
function copy(dst) {
for(var i=1; i<arguments.length; i++) {
var obj = arguments[i];
for(var key in obj) dst[key] = obj[key];
}
return dst;
}
Давайте подробнее разберем происходящее при вызове speedy.found("яблоко"):
- Интерпретатор ищет свойство
foundвspeedy. Ноspeedy— пустой объект, т.к.new Hamsterничего не делает сthis. - Интерпретатор идёт по ссылке
speedy.__proto__ (==Hamster.prototype)и находят там методfound, запускает его. - Значение
thisустанавливается в объект перед точкой, т.е. вspeedy. - Для выполнения
this.food.push()нужно найти свойствоthis.food. Оно отсутствует вspeedy, но есть вspeedy.__proto__. - Значение
"яблоко"добавляется вspeedy.__proto__.food.
У всех хомяков общий живот! Или, в терминах JavaScript, свойство food изменяется в прототипе, который является общим для всех объектов-хомяков.
Заметим, что этой проблемы не было бы при простом присваивании:
this.food = something;
В этом случае значение записалось бы в сам объект, без поиска found в прототипе.
Проблема возникает только со свойствами-объектами в прототипе.
Исправьте её?
Для исправления проблемы нужно дать каждому хомяку свой живот. Это можно сделать, присвоив его в конструкторе.
function Hamster() {
*!*
this.food = [];
*/!*
}
copy(Hamster.prototype, {
// food: [], копируем только метод
found: function(something) {
this.food.push(something)
}
});
speedy = new Hamster();
lazy = new Hamster();
speedy.found("яблоко");
speedy.found("орех");
alert(speedy.food.length) // 2
alert(lazy.food.length) // 0(!)
/* вспомогательная функция copy(dst, src1, src2...) */
function copy(dst) {
for(var i=1; i<arguments.length; i++) {
var obj = arguments[i];
for(var key in obj) dst[key] = obj[key];
}
return dst;
}