При выполнении этого кода вызов rabbit.eat()
запишет в объект свойство full
.
Вопрос — в какой объект: в rabbit
или animal
?
var animal = { };
var rabbit = { };
rabbit.__proto__ = animal;
animal.eat = function() {
this.full = true;
};
rabbit.eat();
Ответ: свойство будет записано в rabbit
.
Если коротко — то потому что this
будет указывать на rabbit
, а прототип при записи не используется.
Если в деталях — посмотрим как выполняется rabbit.eat()
:
Интерпретатор ищет rabbit.eat
, чтобы его вызвать. Но свойство eat отсутствует в объекте rabbit, поэтому он идет по ссылке rabbit.__proto__
и находит это свойство там.
Функция eat
запускается. Контекст ставится равным объекту перед точкой, т.е. this = rabbit
.
Итак — получается, что команда this.full = true
устанавливает свойство full
в самом объекте rabbit
. Итог:
Какие значения будут выводиться в коде ниже?
var animal = { jumps: null };
var rabbit = { jumps: true };
rabbit.__proto__ = animal;
alert( rabbit.jumps ); // ? (1)
delete rabbit.jumps;
alert( rabbit.jumps ); // ? (2)
delete animal.jumps;
alert( rabbit.jumps); // ? (3)
Итого три вопроса.
Есть объекты:
var head = {
glasses: 1
};
var table = {
pen: 3
};
var bed = {
sheet: 1,
pillow: 2
};
var pockets = {
money: 2000
};
Задание состоит из двух частей:
__proto__
так, чтобы любой поиск чего-либо шёл по алгоритму pockets -> bed -> table -> head
.
То есть pockets.pen == 3
, bed.glasses == 1
, но table.money == undefined
.glasses
: обращением к pockets.glasses
или head.glasses
? Попробуйте протестировать.1) Расставим __proto__
:
var head = {
glasses: 1
};
var table = {
pen: 3
};
table.__proto__ = head;
var bed = {
sheet: 1,
pillow: 2
};
bed.__proto__ = table;
var pockets = {
money: 2000
};
pockets.__proto__ = bed;
alert( pockets.pen ); // 3
alert( bed.glasses ); // 1
alert( table.money ); // undefined
2) В современных браузерах, с точки зрения производительности, нет разницы, брать свойство из объекта или прототипа. Они запоминают, где было найдено свойство и в следующий раз при запросе, к примеру, pockets.glasses
начнут искать сразу в прототипе head
.
В примерах ниже производятся различные действия с prototype.
Каковы будут результаты выполнения? Почему?
function Rabbit() { }
Rabbit.prototype = { eats: true };
var rabbit = new Rabbit();
Rabbit.prototype = {};
alert(rabbit.eats);
А если код будет такой? (заменена одна строка):
function Rabbit(name) { }
Rabbit.prototype = { eats: true };
var rabbit = new Rabbit();
Rabbit.prototype.eats = false; // (*)
alert(rabbit.eats);
А такой? (заменена одна строка)
function Rabbit(name) { }
Rabbit.prototype = { eats: true };
var rabbit = new Rabbit();
delete Rabbit.prototype.eats; // (*)
alert(rabbit.eats);
А если бы в последнем коде вместо строки (*) было delete rabbit.eats
?
Итого 4 вопроса.
prototype
всего лишь задаёт __proto__
у новых объектов. Так что его изменение не повлияет на rabbit.__proto__
. Свойство eats
будет получено из прототипа.Rabbit.prototype
и rabbit.__proto__
указывают на один и тот же объект. В данном случае изменения вносятся в сам объект.rabbit.eats
больше взять неоткуда.delete rabbit.eats
попыталось бы удалить eats
из rabbit
, где его и так нет. А чтение в alert
прошло бы из прототипа.Создадим новый объект, вот такой:
function Rabbit() { }
Rabbit.prototype.test = function() { alert(this); }
var rabbit = new Rabbit();
Есть ли разница между вызовами:
rabbit.test();
rabbit.__proto__.test();
Rabbit.prototype.test();
Object.getPrototypeOf(rabbit).test();
Какие из этих вызовов идентичны в браузере IE9+? А в Chrome?
this == rabbit
, остальные ставят this
равным прототипу, следуя правилу «this
— объект перед точкой».
При этом второй вызов не поддерживается в IE, т.к. свойство __proto__
— нестандартное. А третий и четвёртый — идентичны.
В Chrome идентичны три последних вызова.Вы — руководитель команды, которая разрабатывает игру, хомяковую ферму. Один из программистов получил задание создать класс «хомяк» (англ - "Hamster").
Объекты-хомяки должны иметь массив food
для хранения еды и метод found
, который добавляет к нему.
Ниже — его решение. При создании двух хомяков, если поел один — почему-то сытым становится и второй тоже.
В чём дело? Как поправить?
function Hamster() { }
Hamster.prototype.food = [ ]; // пустой "живот"
Hamster.prototype.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 (!??)
Давайте подробнее разберем происходящее при вызове 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 = [];
}
Hamster.prototype.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(!)
Теперь всё в порядке. У каждого хомяка — свой живот.