Prototype chain

Способы создания объектов и получаемые в итоге цепочки прототипов

Создание объектов с помощью литералов

var o = {a: 1};

// Созданный объект 'o' имеет Object.prototype в качестве своего [[Prototype]]
// у 'o' нет собственного свойства 'hasOwnProperty'
// hasOwnProperty — это собственное свойство Object.prototype. 
// Таким образом 'o' наследует hasOwnProperty от Object.prototype
// Object.prototype в качестве прототипа имеет null.
// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];

// Массивы наследуются от Array.prototype 
// (у которого есть такие методы, как indexOf, forEach и т.п.).
// Цепочка прототипов при этом выглядит так:
// a ---> Array.prototype ---> Object.prototype ---> null

function f(){
  return 2;
}

// Функции наследуются от Function.prototype 
// (у которого есть такие методы, как call, bind и т.п.):
// f ---> Function.prototype ---> Object.prototype ---> null

Создание объектов с помощью конструктора

В JavaScript "конструктор" — это "просто" функция, вызываемая с оператором new.

Object.create

В ECMAScript 5 представлен новый метод создания объектов: Object.create. Прототип создаваемого объекта указывается в первом аргументе этого метода:

Используя ключевое словоclass

С выходом ECMAScript 6 появился целый набор ключевых слов, реализующих классы. Они могут показаться знакомыми людям, изучавшим языки, основанные на классах, но есть существенные отличия. JavaScript был и остается прототипно-ориентированным языком. Новые ключевые слова: "class", "constructor", "static", "extends" и "super".

Производительность

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

Кроме того, при циклическом переборе свойств объекта будет обработано каждое свойство, присутствующее в цепочке прототипов.

Если вам необходимо проверить, определено ли свойство усамого объекта, а не где-то в его цепочке прототипов, вы можете использовать методhasOwnProperty, который все объекты наследуют отObject.prototype.

hasOwnProperty— единственная существующая в JavaScript возможность работать со свойствами, не затрагивая цепочку прототипов.

Плохая практика: расширение базовых прототипов

Одной из частых ошибок является расширениеObject.prototypeили других базовых прототипов.

Такой подход называется monkey patching и нарушает принцип инкапсуляции. Несмотря на то, что ранее он использовался в широко распространенных фреймворках, как например, Prototype.js, в настоящее время не существует разумных причин для его использования, поскольку в данном случае встроенные типы "захламляются" дополнительной нестандартной функциональностью.

Единственным оправданием расширения базовых прототипов могут являться лишь полифиллы - эмуляторы новой функциональности (например,Array.forEach) для не поддерживающих её реализаций языка в старых веб-браузерах.

Примеры

B наследует отA:

Важно:

  • Типы определяются в .prototype

  • Для наследования используется Object.create()

prototype и Object.getPrototypeOf

Как уже упоминалось, JavaScript может запутать разработчиков на Java или C++, ведь в нём совершенно нет "нормальных" классов. Всё, что мы имеем - лишь объекты. Даже те "classes", которые мы имитировали в статье, тоже являются функциональными объектами.

Вы наверняка заметили, что у function A есть особое свойство prototype. Это свойство работает с оператором new. Ссылка на объект-прототип копируется во внутреннее свойство[[Prototype]]нового объекта. Например, в этом случае var a1 = new A() JavaScript (после создания объекта в памяти и до выполнения функции function A()) устанавливает a1.[[Prototype]] = A.prototype. Потом, при попытке доступа к свойству нового экземпляра объекта, JavaScript проверяет, принадлежит ли свойство непосредственно объекту. Если нет, то интерпретатор ищет в свойстве [[Prototype]]. Всё, что было определено в prototype, в равной степени доступно и всем экземплярам данного объекта. При внесении изменений в prototype , все эти изменения сразу же становятся доступными и всем экземплярам объекта.

[[Prototype]] работает рекурсивно, то есть при вызове:

JavaScript на самом деле выполняет что-то подобное:

а когда вы делаете так:

JavaScript проверяет, есть ли у o свойство someProp и если нет, то проверяет Object.getPrototypeOf(o).someProp ,а если и там нет, то ищет в Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp и так далее.

Last updated