Источник: 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
для перебора свойств именно самого объекта, без прототипа.