Прототипы объектов. Свойство prototype

Прототип объекта

В JavaScript объекты имеют специальное скрытое свойство [[Prototype]] (так оно названо в спецификации), которое либо равно null, либо ссылается на другой объект, который называется «прототипом». Свойство [[Prototype]] в JS используется для реализации наследования (прототипирования).

Прототипирование — это механизм, с помощью которого объекты JavaScript наследуют свойства друг от друга.

JavaScript часто описывают как язык прототипного наследования — каждый объект имеет объект-прототип, который выступает шаблоном (родительским объектом) и от которого объект наследует методы и свойства.

Объект-прототип также может иметь свой прототип и наследовать его свойства и методы и так далее (цепочка прототипов объясняет доступность объектам свойств и методов, которые определены в других объектах).

ВАЖНО! Методы и свойства в цепочке прототипов не копируются из одного объекта в другой: к ним обращаются, поднимаясь вверх по цепочке.

Устаревшее свойство __proto__ объектов:

  1. является производным от свойства prototype конструктора и обеспечивает в JavaScript связь между экземпляром объекта и его прототипом;
  2. является геттером/сеттером для [[Prototype]] (таким образом делая его изменяемым).

В настоящее время свойство __proto__ заменяют функции Object.getPrototypeOf() и Object.setPrototypeOf().

Проверка принадлежности объекта определенному класcу (оператор instanceof )

Оператор instanceof проверяет, принадлежит ли объект к определённому классу (проверяет, присутствует ли объект constructor.prototype в цепочке прототипов object).

Синтаксис:

Например:

Важноinstanceof выполняет проверку типов совсем не так, как это происходит в строго типизированных языках. Вместо этого, он проверяет на идентичность прототипу, и его легко обмануть.

Например, он не будет работать в разных контекстах выполнения (распространённый источник ошибок и ненужных ограничений). Для справки, пример с библиотекой bacon.js. Поскольку это проверка идентичности свойства prototype целевого объекта, это может приводить к неочевидным вещам, например:

Этот результат полностью соответствует спецификации JavaScript: просто instanceof не может дать никаких гарантий относительно безопасности типов.

[свернуть]

Свойство prototype

Наследуемые свойства и методы объекта, созданного с помощью конструктора, могут быть определены в свойстве конструктора prototype.

Значение свойства prototype — это объект, который в основном представляет собой контейнер для хранения свойств и методов, которые мы хотим наследовать объектами, расположенными дальше по цепочке прототипов.

Таким образом, например, Object.prototype.valueOf() и т. п. доступны для любых типов объектов, которые наследуются от Object.prototype, включая новые экземпляры объектов, созданные из конструктора.

Object.is(), Object.keys() и другие члены, не определённые в контейнере prototype, не наследуются экземплярами объектов или типами объектов, которые наследуются от Object.prototype: это методы (свойства), доступные только в конструкторе Object().

Примеры

[свернуть]

Различие между прототипом объекта и свойством prototype  функции-конструктора:

  • прототип объекта является свойством каждого экземпляра (и доступен через Object.getPrototypeOf(obj) или через устаревшее свойство __proto__);
  • свойство prototype в функциях-конструкторах является свойством конструктора (т.е. Object.getPrototypeOf(new Foobar()) относится к тому же объекту, что и Foobar.prototype).
Создание нового объекта с помощью функции-конструктора

[свернуть]
Создание нового объекта с помощью метода Object.create()

[свернуть]

Изменение прототипа [[Prototype]] объекта является очень медленной операцией, это справедливо для любого браузера и движка JavaScript. Изменение прототипов может распространяться на любой код, который имеет доступ к любому объекту, чей прототип [[Prototype]] был изменён. Если вы заботитесь о производительности, вы никогда не должны изменять прототип [[Prototype]] объекта. Вместо этого создайте объект с нужным прототипом [[Prototype]] с помощью метода Object.create().

Возвращение прототипа объекта ( метод Object.getPrototypeOf() )

Метод Object.getPrototypeOf() возвращает прототип (то есть внутреннее свойство [[Prototype]]) указанного объекта.

Синтаксис метода Object.getPrototypeOf():

Параметры метода Object.getPrototypeOf():

obj — объект, прототип которого будет возвращён.

[свернуть]
Изменение прототипа объекта ( метод Object.getPrototypeOf() )

Метод Object.setPrototypeOf()  изменяет прототип (внутреннее свойство [[Prototype]]) указанного объекта на другой объект или на null.

Синтаксис метода Object.setPrototypeOf():

Параметры метода Object.setPrototypeOf():

obj — объект, которому устанавливается новый прототип prototype.

Возвращаемое значение: новый прототип объекта (объект или null).

ВАЖНО! Изменение прототипа [[Prototype]] объекта является очень медленной операцией, это справедливо для любого браузера и движка JavaScript. Изменение прототипов может распространяться на любой код, который имеет доступ к любому объекту, чей прототип [[Prototype]] был изменён. Если вы заботитесь о производительности, вы никогда не должны изменять прототип [[Prototype]] объекта. Вместо этого создайте объект с нужным прототипом [[Prototype]] с помощью метода Object.create().

[свернуть]

Свойство constructor

Каждая функция-конструктор имеет свойство prototype, значением которого является объект, содержащий свойство constructor и указывающий на исходную функцию-конструктор. Cвойства, определённые в свойстве obj.prototype становятся доступными для всех объектов экземпляра, созданных с помощью функции-конструктора. Конструктор — это функция, поэтому её можно вызвать с помощью круглых скобок; вам просто нужно включить ключевое слово new, чтобы указать, что вы хотите использовать эту функцию в качестве конструктора.

Для создания другого экземпляра объекта из конструктора необходимо поместить круглые скобки, содержащие любые требуемые параметры, в конец свойства constructor()

Свойство constructor позволяет также, например, вернуть имя конструктора экземпляра объекта:

Значение constructor.name может измениться (из-за прототипического наследования, привязки, препроцессоров, транспилеров и т. д.), Поэтому для более сложных примеров можно использовать оператор instanceof,  который проверяет, принадлежит ли объект к определённому классу (присутствует ли объект constructor.prototype в цепочке прототипов object).

Работа ключевого слова new

  1. создаёт новый экземпляр класса;
  2. связывает this с новым экземпляром класса;
  3. создаёт ссылку нового объекта [[Prototype]] на объект, на который ссылается свойство функции конструктора prototype;
  4. создаёт ссылку нового свойства constructor объекта на конструктор, который был вызван;
  5. именует тип объекта после конструктора, который вы сможете заметить в консоли отладки (например, [Object Foo] вместо [Object object]);
  6. позволяет оператору instanceof проверить, является ли ссылка на прототип объекта тем же самым объектом, на который ссылается свойство prootype конструктора.

Изменение прототипов

Методы, добавленные в прототип, становятся доступны для всех экземпляров объектов, созданных конструктором:

Особенности определения свойств и методов в prototype

Свойства, определённые в prototype, не очень гибки при таком определении. Например, вы можете добавить свойство следующим образом:

Однако, было бы намного лучше создать fullName из name.first и name.last:

Однако это не работает, поскольку в этом случае this будет ссылаться на глобальную область, а не на область функции. Вызов этого свойства вернёт undefined undefined. Таким образом, внутри конструктора целесообразно определять постоянные свойства прототипа (т. е. те, которые никогда не нуждаются в изменении).

Довольно распространённый шаблон, упрощающий чтение кода — это:

  • определение свойств внутри конструктора (конструктор содержит только определения свойств) и
  • определение методов в прототипе (методы вынесены в отдельные блоки и не хранятся в каждом экземпляре класса; обращение к ним происходит по цепочке прототипирования).

Например (отсюда):

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.