Источник: http://learn.javascript.ru/prototype
Смысл прототипного наследования в том, что один объект можно сделать прототипом другого. При этом если свойство не найдено в объекте — оно берётся из прототипа.
__proto__
Наследование в JavaScript реализуется при помощи специального свойства __proto__
.
Если один объект, например, rabbit
, имеет специальную ссылку __proto__
на другой объект animal
, то все свойства, которые ищутся в rabbit
, будут затем искаться в animal
.
var animal = { eats: true };
var rabbit = { jumps: true };
rabbit.__proto__ = animal; // унаследовать
alert(rabbit.eats); // true
alert(rabbit.jumps); // true
Когда запрашивается свойство rabbit
, интерпретатор ищет его сначала в самом объекте rabbit
, а если не находит — в объекте rabbit.__proto__
, то есть, в данном случае, в animal
.
Объект, на который указывает __proto__
, называется его «прототипом».
Нет никаких ограничений на объект, который записывается в прототип. Например:
var rabbit = { };
rabbit.__proto__ = window;
rabbit.open('http://google.com'); // вызовет метод open из window
Если вы будете читать спецификацию EcmaScript — свойство __proto__
обозначено в ней как [[Prototype]]
. Не путать со свойством prototype
.
Прототип используется только если свойство не найдено.
var animal = { eats: true };
var fedUpRabbit = { eats: false };
fedUpRabbit.__proto__ = animal;
alert(fedUpRabbit.eats); // false, свойство взято из fedUpRabbit
Другими словами, прототип — это «резервное хранилище свойств и методов» объекта, автоматически используемое при поиске.
У объекта, который является __proto__
, может быть свой __proto__
, у того — свой, и так далее.
Например, цепочка наследования из трех объектов donkey -> winnie -> owl
:
var owl = {
sum: function(a, b) {
return a + b;
}
}
var winnie = { /* ... */ };
winnie.__proto__ = owl;
var donkey = { /* ... */ }
donkey.__proto__ = winnie;
alert( donkey.sum(2,2) ); // "4" ответит owl
Картина происходящего:
Свойство __proto__
доступно в явном виде не во всех браузерах.
Поэтому используются другие способы для создания объектов с данным прототипом.
Вызов Object.create(proto)
создаёт пустой объект с данным прототипом proto
.
Например:
var animal = { eats: true };
var rabbit = Object.create(animal);
alert(rabbit.eats); // true
Этот код создал пустой объект rabbit
с прототипом animal
:
Мы можем добавить свойства в новый объект rabbit
:
var animal = { eats: true };
var rabbit = Object.create(animal);
rabbit.jumps = true;
Станет:
Этот метод позволяет только создать новый пустой объект. Он не может изменить прототип существующего объекта.
Созданием объектов часто занимается функция-конструктор. Чтобы таким объектам автоматически ставить прототип, существует свойство prototype
.
Свойство prototype можно указать на любом объекте, но особый смысл оно имеет, если назначено функции.
При создании объекта через new, в его прототип __proto__
записывается ссылка из prototype функции-конструктора.
Например:
var animal = { eats: true }
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
var rabbit = new Rabbit('John');
alert( rabbit.eats ); // true, т.к. rabbit.__proto__ == animal
Установка Rabbit.prototype = animal
говорит интерпретатору следующее: «запиши __proto__ = animal
при создании объекта через new Rabbit
».
Значением prototype
может быть только объект.
Вызов Object.create(proto)
, который создаёт пустой объект с данным прототипом, можно эмулировать, так что он будет работать во всех браузерах, включая IE6+.
Кросс-браузерный аналог — inherit
состоит буквально из нескольких строк:
function inherit(proto) {
function F() {}
F.prototype = proto;
var object = new F;
return object;
}
F
. Она ничего не делает с this
, так что вызов new F
вернёт пустой объект.F.prototype
устанавливается в будущий прототип proto
;new F
будет пустой объект с __proto__
равным значению F.prototype
.Результат вызова inherit(animal)
идентичен Object.create(animal)
. Это будет новый пустой объект с прототипом animal
.
Например:
var animal = { eats: true };
var rabbit = inherit(animal);
alert(rabbit.eats); // true
Вызов Object.getPrototypeOf(obj)
возвращает прототип obj
.
Не поддерживается в IE8-.
var animal = {
eats: true
};
rabbit = Object.create(animal);
alert( Object.getPrototypeOf(rabbit) === animal ); // true
Этот метод даёт возможность получить __proto__
. но не изменить его.
Метод obj.hasOwnProperty(prop)
есть у всех объектов. Он позволяет проверить, принадлежит ли свойство самому объекту, без учета его прототипа.
Например:
var animal = {
eats: true
};
rabbit = Object.create(animal);
rabbit.jumps = true;
alert( rabbit.hasOwnProperty('jumps') ); // true: jumps принадлежит rabbit
alert( rabbit.hasOwnProperty('eats') ); // false: eats не принадлежит
Цикл for..in
перебирает все свойства в объекте и его прототипе.
Например:
var animal = {
eats: true
};
rabbit = Object.create(animal);
rabbit.jumps = true;
for (var key in rabbit) {
alert (key + " = " + rabbit[key]); // выводит и "eats" и "jumps"
}
Иногда хочется посмотреть, что находится именно в самом объекте, а не в прототипе.
Это можно сделать, если профильтровать key
через hasOwnProperty
:
var animal = {
eats: true
};
rabbit = Object.create(animal);
rabbit.jumps = true;
for (var key in rabbit) {
if ( !rabbit.hasOwnProperty(key) ) continue; // пропустить "не свои" свойства
alert (key + " = " + rabbit[key]); // выводит только "jumps"
}
Мы рассмотрели основы наследования в JavaScript. Упорядочим эту информацию.
__proto__
(в спецификации обозначено [[Prototype]]
).
Оно работает так: при попытке получить свойство из объекта, если его там нет, оно ищется в __proto__
объекта.this
с прототипами никак не связаны, они работают по своим правилам.Установка прототипа __proto__
:
obj.__proto__
. Эта нестандартная возможность бывает полезна в целях отладки.__proto__
равным своему prototype
.Object.create(proto)
создаёт пустой объект с прототипом proto
.В IE8- этого метода нет, но его можно эмулировать при помощи следующей функции inherit
:
function inherit(proto) {
function F() {}
F.prototype = proto;
return new F;
}
Можно даже присвоить Object.create = inherit
, чтобы иметь унифицированный вызов, но при этом стоит иметь в виду, что современные браузеры поддерживают также дополнительный второй аргумент Object.create
, позволяющий задать свойства объекта, а inherit
— нет.
Кроме того, есть следующие методы для работы с прототипом:
Object.getPrototypeOf(obj)
— получить прототип объекта obj
, кроме IE8-obj.hasOwnProperty(prop)
— возвращает true, если prop
является свойством объекта obj
, без учёта прототипа.Проверка obj.hasOwnProperty
используется, в частности, в for..in
для перебора свойств именно самого объекта, без прототипа.