function Hamster() { }
Hamster.prototype = {
food: [],
something: 'no',
something1: {p:0},
found: function(something) {
this.food.push(something)
this.something = something;
this.something1 = {p:Math.random()};
}
};
speedy = new Hamster();
lazy = new Hamster();
speedy.found("apple");
speedy.found("orange");
alert([
'Speedy: ' + speedy.food,
'Lazy: ' + lazy.food,
'Prototype: ' + Hamster.prototype.food
].join('\n'));
alert([
'Speedy: ' + JSON.stringify(speedy.something1),
'Lazy: ' + JSON.stringify(lazy.something1),
'Prototype: ' + JSON.stringify(Hamster.prototype.something1)
].join('\n'));
alert([
'Speedy: ' + speedy.something,
'Lazy: ' + lazy.something,
'Prototype: ' + Hamster.prototype.something
].join('\n'));
Все дело в том, что food - массив (и, соответственно, объект). При обращении к this.food JS сначала пойдет искать свойство food у самого объекта, а потом, не найдя его, пойдет по цепочке прототипов (напоминаю, что при использовании оператора new ссылка на объект-прототип просто записывается во внутреннее свойство [[Prototype]]). В Hamster.prototype он найдет искомое значение и вернет ссылку на него. Ссылку на food из прототипа (!). Не удивительно, что изменится food в прототипе, а не объекте (в объекте свойства food, вообще говоря, нет).
Рассмотрим подробнее, почему так происходит.
1. JS "считывает" this.food.
2. Применяется
оператор доступа к свойству. Мы получаем Refernce-объект с базой this (на самом деле не this, а именно тот объект, на который он ссылается) и именем свойства food.
3. Далее "считывается" .push.
4. Снова тот же оператор, и мы получаем в качестве базы
значение предыдущего Reference-объекта и именем свойства push. Теперь подробнее о значении Reference-объекта: в ходе выполнения
[[Get]]
JS не находит значения food как собственного свойства объекта и идет в объект-прототип [[Prototype]]. Соответствено, значением Reference-объекта будет объект food прототипа, т.е. [[Prototype]].food.
5. Считываются и обрабатываются аргументы метода, это нам сейчас не важно.
6.
Вызывается метод. В качестве this берется база нового Reference-объекта (см. шаг 6). А эта самая база указывает на [[Prototype]].food. Соответственно, и изменения будут проведены над этим объектом.
Казалось бы, почему тогда поведение something отличается? Тут все дело в том, что мы не обращаемся к свойству
1. JS "считывает" this.something.
2. Применяется
оператор доступа к свойству. Мы получаем Refernce-объект с базой this и именем свойства something.
3. Далее "считывается" оператор
присваивания.
4. В ходе
записи значения в Reference вызывается метод [[Put]] объекта, который лежит у нас в базе (а это - тот самый, на кого указывает this), и этот метод записывает присваиваемое значение свойству самого объекта, а не его прототипа. По-сути, его совсем не волнует, что такое something и откуда оно пришло. Он просто запишет нужное свойство. В случае с массивом нам пришлось обратиться к значению this.food, когда мы использовали оператор обращения к свойству.