Показать сообщение отдельно
  #2 (permalink)  
Старый 11.02.2011, 15:49
Аватар для B@rmaley.e><e
⊞ Развернуть
Отправить личное сообщение для B@rmaley.e><e Посмотреть профиль Найти все сообщения от B@rmaley.e><e
 
Регистрация: 11.01.2010
Сообщений: 1,810

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, когда мы использовали оператор обращения к свойству.
Ответить с цитированием