что такое прототипное наследование в javascript

Наследование и цепочка прототипов

Модель наследования в JavaScript может озадачить опытных разработчиков на высокоуровневых объектно-ориентированных языках (таких, например, как Java или C++), поскольку она динамическая и не включает в себя реализацию понятия class (хотя ключевое слово class, бывшее долгие годы зарезервированным, и приобрело практическое значение в стандарте ES2015, однако, Class в JavaScript ES>=6 представляет собой лишь «синтаксический сахар» поверх прототипно-ориентированной модели наследования).

Хотя прототипную модель наследования некоторые относят к недостаткам JavaScript, на самом деле она мощнее классической. К примеру, поверх неё можно предельно просто реализовать классическое наследование, а вот попытки совершить обратное непременно вынудят вас попотеть.

Наследование с цепочкой прототипов

Наследование свойств

Объекты в JavaScript — динамические «контейнеры», наполненные свойствами (называемыми собственными свойствами). Каждый объект содержит ссылку на свой объект-прототип.
При попытке получить доступ к какому-либо свойству объекта, свойство вначале ищется в самом объекте, затем в прототипе объекта, после чего в прототипе прототипа, и так далее. Поиск ведётся до тех пор, пока не найдено свойство с совпадающим именем или не достигнут конец цепочки прототипов.

При добавлении к объекту нового свойства, создаётся новое собственное свойство (own property). Единственным исключением из этого правила являются наследуемые свойства, имеющие getter или setter.

Наследование «методов»

В области видимости унаследованной функции ссылка this указывает на наследующий объект (на наследника), а не на прототип, в котором данная функция является собственным свойством.

Источник

Понимание ООП на джаваскрипте (ES5), часть 2

что такое прототипное наследование в javascript. image loader. что такое прототипное наследование в javascript фото. что такое прототипное наследование в javascript-image loader. картинка что такое прототипное наследование в javascript. картинка image loader. Модель наследования в JavaScript может озадачить опытных разработчиков на высокоуровневых объектно-ориентированных языках (таких, например, как Java или C++), поскольку она динамическая и не включает в себя реализацию понятия class (хотя ключевое слово class, бывшее долгие годы зарезервированным, и приобрело практическое значение в стандарте ES2015, однако, Class в JavaScript ES>=6 представляет собой лишь "синтаксический сахар" поверх прототипно-ориентированной модели наследования).

Поднимаю продолжение заброшенного перевода, поскольку вопросы в оригинале вплотную переплетаются с вопросами наследования, сделанными в собственной компактной библиотеке для использования без фреймворков, имеющих поддержку ООП. Пусть то и другое не оригинально, но вместе даёт понимание работы наследования.

Для полноты статьи и единого стиля, перевод начинается с вопросов наследования, несмотря на то, что они уже были упомянуты в конце первой части. Далее рассматриваются разнообразные задачи наследования так, как их рассмотрел автор. Надо сказать, что автор широко использует новые конструкции ES5 (объяснив это в конце), которые работают не во всех браузерах и заслоняют от понимания реализацию их на низком уровне языка, на котором они изначально применялись. Для настоящего понимания наследования следует обратиться к более глубокому разбору реализаций или к реализациям методов-обёрток из ES5: Object.create, Object.defineProperty, Function.bind, get и set literals, Object.getOwnPropertyNames, Object.defineProperty, Object.getOwnPropertyDescriptor, Object.getPrototypeOf. Часть их разбирается в статье (Object.create, get и set, Object.defineProperty, bind), но не всегда в порядке появления. Таким образом, статья стремится преподнести не реализацию наследования вообще, а ту реализацию, которую успели формализовать в рабочем черновике стандарта EcmaScript 5. Это лучше, чем ничего, но несколько меньше, чем полное понимание реализаций наследования.

3. Прототипное наследование

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

Далее в игру вступает наследование. Оно лучше разделяет понятия, когда объекты наделяются своими методами на основе методов других объектов.

Прототипное наследование идёт дальше и может избирательно расширять методы, описывать общее поведение и использовать другие занятные приёмы, которых мы коснёмся. Печалит лишь то, что модель наследования в JS немного ограничена, и для обхода трудностей эти приёмы будут временами избыточны выносить мозг.

3.1. Прототипы

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

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

Однако, клонирование не означает, что вы будете иметь различные копии функций или данных. На самом деле, в JS реализовано наследование через делегирование: все свойства хранятся у родителя, а наследникам дают попользоваться.

Наш пример пока что хорошо укладывается в эту модель. Например, методы имени и приветствия могут быть описаны в отдельном объекте и показаны там, где надо. Что приводит нас к следующей модели:

3.2. Как работает [[Prototype]]

Как видно из примера, ни одно свойство из person не было упомянуто в mikhail, но все они прекрасно работают, потому что в JS передаются (делегируются) доступы к свойствам, т.е. свойства ищутся во всех родителях объекта.

Когда свойство запрашивается из объекта, интерпретатор проверяет собственные свойства объекта. Если такое свойство отсутствует, проверяется родитель, и так — до конца цепочки родителей или до первого существующего свойства.

Если изменяем свойство прототипа, оно немедленно изменится для всех прототипов других объектов-наследников.

3.3. Переопределение свойств

Таким образом, прототипы и наследование используется для разделения доступа к данным для разных объектов и выполняется очень быстро и эффективно по затратам памяти, поскольку используется один источник данных для всех наследников.

Что, если нужно добавить специализированные методы на основе данных, которые имелись в момент наследования? Мы видели раньше, что методы определяются на основе свойств, поэтому будем определять особое поведение тем же способом — просто присваивать новые свойства.

Для демонстрации предположим, что Person реализует общее приветствие, а наследники Person — определяют собственные. Кроме того, добавим ещё одного человека, чтобы увидеть разницу.

Заметьте, что mikhail и kristin имеют индивидуальные приветствия, выражаемые версиями метода greet.

3.4 Миксины (примеси)

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

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

К примеру, множественное наследование позволило бы использовать объекты — источники данных, дающие настройки методов или свойства по умолчанию.

К счастью, поскольку мы напрямую определяем методы объектов, мы можем решать эти проблемы примесями — некоторым дополнительным определением объектов во время их создания.

Что есть примеси? Можно сказать, они — «безродные», неунаследованные ниоткуда объекты. Они полностью определены в своих свойствах-методах и, чаще всего, сделаны для включения в другие объекты (хотя, их методы могли бы использоваться напрямую).

Развивая нашу небольшую модель персонажей, давайте добавим им некоторые способности. Пусть человек может быть пианистом или певцом — иметь методы pianist или singer в произвольных сочетаниях. Этот случай не укладывается в прототипную модель, поэтому пойдём на небольшой трюк. (На самом деле, можно заменить миксины переменной цепочкой наследований с прототипами, поэтому выбор миксина — это вопрос удобства и оптимальной реализации, а не следствие невыполнимости в модели прототипов. — прим.перев.)

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

extend() здесь перебирает собственные свойства source и копирует их в target. Отметим, что target будет изменяться, для него эта функция — разрушительная, что обычно — не проблема. Важнее то, что она наименее затратна.

Теперь можем добавлять «способности» к нашим объектам.

3.5. Доступ к экранированным свойствам

Мы научились наследовать свойства и расширять их миксинами. Теперь есть небольшая проблема: что делать, если хотим получить доступ к перезаписанному (экранированному) свойству родительского объекта?

JS предоставляет функцию Object.getPrototypeOf которая возвращает [[Prototype]]. Поэтому доступ к свойствам прототипа достаточно прост:

Можно было бы навно предположить, что достаточно обращения к прототипу контекста (this):

Выглядит хорошо, но есть загвоздка: если попытаться применить подход не к непосредственному предку, возникнет бесконечная рекурсия из-за того, что this видит всегда ближайший контекст функции и будет попадать на один и тот же родительский объект, как проиллюстрировано:

Простое решение — брать прототип из родительского объекта, а не из текущего. Последний пример становится такимчто такое прототипное наследование в javascript. image loader. что такое прототипное наследование в javascript фото. что такое прототипное наследование в javascript-image loader. картинка что такое прототипное наследование в javascript. картинка image loader. Модель наследования в JavaScript может озадачить опытных разработчиков на высокоуровневых объектно-ориентированных языках (таких, например, как Java или C++), поскольку она динамическая и не включает в себя реализацию понятия class (хотя ключевое слово class, бывшее долгие годы зарезервированным, и приобрело практическое значение в стандарте ES2015, однако, Class в JavaScript ES>=6 представляет собой лишь "синтаксический сахар" поверх прототипно-ориентированной модели наследования).:

Способ не лишён недостатков: объект жёстко задан в функции, и мы не можем так просто взять, и применить функцию к любому объекту, как было до сих пор. Функция будет зависима от предка объекта, а не от него самого.

Подход, предложенный в новой версии JS, решает только первую часть задачи, что самое простое. Здесь мы будем делать то же самое, но введением другого способа определения методов. Да, методов, а не общих функций.

Функции для доступа к свойствам в [⁣[Prototype]⁣] требуют дополнительной информации: объекта, где они записаны. Это требует поискового алгоритма, работающего со статическими данными, но решает наши рекурсивные проблемы.

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

Источник

JavaScript — шаблоны наследования

Примечание переводчика: Тема наследования в JavaScript является одной из самых тяжелых для новичков. С добавлением нового синтаксиса с ключевым словом class, понимание наследования явно не стало проще, хотя кардинально нового ничего не появилось. В данной статье не затрагиваются нюансы реализации прототипного наследования в JavaScript, поэтому если у читателя возникли вопросы, то рекомендую прочитать следующие статьи: Основы и заблуждения насчет JavaScript и Понимание ООП в JavaScript [Часть 1]

По всем замечаниям, связанным с переводом, обращайтесь в личку.

JavaScript является очень мощным языком. Настолько мощным, что в нем сосуществует множество различных способов проектирования и создания объектов. У каждого способа есть свои плюсы и минусы и я бы хотел помочь новичкам разобраться в этом. Это продолжение моего предыдущего поста, Хватит «классифицировать» JavaScript. Я получил много вопросов и комментариев с просьбами привести примеры, и для именно этой цели я решил написать эту статью.

JavaScript использует прототипное наследование

Это означает, что в JavaScript объекты наследуются от других объектов. Простые объекты в JavaScript, созданные с использованием <> фигурных скобок, имеют только один прототип: Object.prototype. Object.prototype, в свою очередь тоже объект, и все свойства и методы Object.prototype доступны для всех объектов.

Массивы, созданные с помощью [] квадратных скобок, имеют несколько прототипов, в том числе Object.prototype и Array.prototype. Это означает, что все свойства и методы Object.prototype и Array.prototype доступны для всех массивов. Одноименные свойства и методы, например .valueOf и .ToString, вызываются из ближайшего прототипа, в этом случае из Array.prototype.

Определения прототипа и создание объектов

Способ 1: Шаблон конструктор

JavaScript имеет особый тип функции называемых конструкторами, которые действуют так же, как и конструкторы в других языках. Функции-конструкторы вызываются только с помощью ключевого слова new и связывают создаваемый объект с контекстом функции-конструктора через ключевое слово this. Типичный конструктор может выглядеть следующим образом:

Использование этого конструктора выглядит также как и создание объекта в других языках:

bark и print методы прототипа, которые применяются для всех объектов созданных с помощью конструктора Dog. Свойства name и breed инициализируются в конструкторе. Это общепринятая практика, когда все методы определяются в прототипе, а свойства инициализируются конструктором.

Способ 2: Определение класса в ES2015 (ES6)

Ключевое слово class было зарезервировано в JavaScript с самого начала и вот наконец-то пришло время его использовать. Определения классов в JavaScript схоже с другими языками.

Многие люди считают этот синтаксис удобным, потому что он объединяет в одном блоке конструктор и объявление статичных и прототипных методов. Использование точно такое же, как и в предыдущем способе.

Способ 3: Явное объявление прототипа, Object.create, фабричный метод

Этот способ показывает, что на самом деле новый синтаксис с ключевым словом class использует прототипное наследование. Также этот способ позволяет создать новый объект без использования оператора new.

Этот синтаксис удобен, потому что прототип объявляется явно. Понятно что определено в прототипе, а что определено в самом объекте. Метод Object.create удобен, потому что он позволяет создать объект от указанного прототипа. Проверка с помощью .isPrototypeOf по-прежнему работает в обоих случаях. Использование разнообразно, но не чрезмерно:

Способ 4: Object.create, фабрика верхнего уровня, отложенный прототип

Этот способ является небольшим изменение способа 3, где сам класс является фабрикой, в отличии от случая когда класс является объектом с фабричным методом. Похоже, на пример конструктора (способ 1), но использует фабричный метод и Object.create.

Этот способ интересен тем, что похож на первой способ, но не требует ключевого слова new и работает с оператором instanceOf. Использование такое же, как и в первом способе, но без использования ключевого слова new:

Сравнение

Способ 1 против Способа 4

Существует довольно мало причин, для того чтобы использовать Способ 1 вместо Способа 4. Способ 1 требует либо использование ключевого слова new, либо добавление следующей проверки в конструкторе:

В этом случае проще использовать Object.create с фабричным методом. Вы также не можете использовать функции Function#call или Function#apply с функциями-конструкторами, потому что они переопределяют контекст ключевого слова this. Проверка выше, может решить и эту проблему, но если вам нужно работать с неизвестным заранее количеством аргументов, вы должны использовать фабричный метод.

Способ 2 против Способа 3

Те же рассуждения о конструкторах и операторе new, что были упомянуты выше, применимы и в этом случае. Проверка с помощью instanceof необходима, если используется новый синтаксис class без использования оператора new или используются Function#call или Function#apply.

Мое мнение

«Простое лучше мудреного», и использование классов, потому что оно считается более «изощренным» является просто ненужной, технической головомойкой.

Ключевое слово class, возможно будет наиболее пагубной чертой в JavaScript. Я испытываю огромное уважение к блестящим и очень трудолюбивым людям, которые были вовлечены в процесс написания стандарта, но даже блестящие люди иногда делают неправильные вещи. — Eric Elliott

Добавление чего-то ненужного и возможно пагубного, противоречащего самой природе языка является необдуманным и ошибочным.
Если вы решите использовать class, я искренне надеюсь, что мне никогда не придется работать с вашим кодом. На мой взгляд, разработчики должны избегать использования конструкторов, class и new, и использовать методы, которые более естественны парадигме и архитектуре языка.

Источник

Наследование в JavaScript

Базовая компьютерная грамотность, понимание основ HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Структурные элементы) and основы Объектно-ориентированного JS (см. Введение в объекты).

Необходимые знания:
Цель:Понять, как можно реализовать наследование в JavaScript.

Прототипное наследование

Давайте рассмотрим, как это сделать на конкретном примере.

Начало работы

Все методы определены в прототипе конструктора. Например:

Определение функции-конструктора Teacher()

В качестве примечания мы могли бы просто сделать это:

Наследование от конструктора без параметров

Установка Teacher()’s prototype и конструктор ссылок

Предоставление Teacher() новой функции greeting()

Это выводит на экран приветствие учителя, в котором используется соответствующий префикс имени для своего пола, разработанный с использованием условного оператора.

Попробуйте пример

Примечание. Если вам не удаётся заставить это работать, сравните свой код с нашей готовой версией (см. также рабочее демо).

Вам также может быть интересно узнать некоторые из новых функций ECMAScript, которые позволяют нам делать наследование более чисто в JavaScript (см. Classes). Мы не рассматривали их здесь, поскольку они пока не поддерживаются очень широко в браузерах. Все остальные конструкторы кода, которые мы обсуждали в этом наборе статей, поддерживаются ещё в IE9 или ранее и есть способы добиться более ранней поддержки, чем это.

Дальнейшее упражнение

Примечание. Если вам не удаётся заставить это работать, сравните свой код с нашей готовой версией (см. также рабочее демо).

Object member summary

Подводя итог, вы в основном получили три типа свойств / методов, о которых нужно беспокоиться:

Если вы не уверены, что это такое, не беспокойтесь об этом, пока вы ещё учитесь и знание придёт с практикой.

Когда вы используете наследование в JavaScript?

В частности, после этой последней статьи вы можете подумать: «У-у-у, это сложно». Ну, ты прав. Прототипы и наследование представляют собой некоторые из самых сложных аспектов JavaScript, но многие возможности и гибкость JavaScript вытекают из его структуры объектов и наследования и стоит понять, как это работает.

В некотором смысле вы используете наследование все время. Всякий раз, когда вы используете различные функции веб-API или методы/свойства, определённые во встроенном объекте браузера, который вы вызываете в своих строках, массивах и т.д., вы неявно используете наследование.

Что касается использования наследования в вашем собственном коде, вы, вероятно, не будете часто его использовать, особенно для начала и в небольших проектах. Это пустая трата времени на использование объектов и наследование только ради этого, когда они вам не нужны. Но по мере того, как ваши базы кода становятся больше, вы с большей вероятностью найдёте необходимость в этом. Если вы начинаете создавать несколько объектов с подобными функциями, то создание универсального типа объекта, содержащего все общие функции и наследование этих функций в более специализированных типах объектов, может быть удобным и полезным.

Примечание. Из-за того, как работает JavaScript, с цепочкой прототипов и т.д., совместное использование функций между объектами часто называется делегированием. Специализированные объекты делегируют функциональность универсальному типу объекта.

При использовании наследования вам рекомендуется не иметь слишком много уровней наследования и тщательно отслеживать, где вы определяете свои методы и свойства. Можно начать писать код, который временно изменяет прототипы встроенных объектов браузера, но вы не должны этого делать, если у вас нет действительно веской причины. Слишком много наследования могут привести к бесконечной путанице и бесконечной боли при попытке отладки такого кода.

Резюме

В этой статье мы рассмотрели оставшуюся часть основной теории и синтаксиса OOJS, которые, как мы думаем, вам следует знать сейчас. На этом этапе вы должны понимать основы JavaScript, ООП, прототипы и прототипное наследование, как создавать классы (конструкторы) и экземпляры объектов, добавлять функции в классы и создавать подклассы, которые наследуются от других классов.

В следующей статье мы рассмотрим, как работать с JavaScript Object Notation (JSON), общим форматом обмена данными, написанным с использованием объектов JavaScript.

Источник

Прототипы и наследование в JavaScript

JavaScript – это язык, основанный на прототипах. Это значит, что свойства и методы объектов можно повторно использовать посредством общих объектов, которые можно клонировать и расширять. Это называется наследованием прототипов и отличается от наследования классов. Среди популярных объектно-ориентированных языков программирования JavaScript относительно уникален, поскольку другие известные языки (PHP, Python и Java) являются языками на основе классов, которые в качестве макетов для объектов используют классы вместо прототипов.

В этом мануале вы узнаете, что такое прототипы объектов, наследование и цепочки прототипов и как использовать функцию-конструктор для расширения прототипов в новых объектах.

Прототипы в JavaScript

В мануале Объекты в JavaScript вы уже ознакомились с объектами и научились создавать их, изменять и извлекать их свойства. Теперь вы научитесь использовать прототипы для расширения объектов.

Каждый объект в JavaScript имеет внутреннее свойство, называемое [[Prototype]]. Для примера попробуйте создать новый пустой объект.

Так создается объект обычно, но есть и другой способ сделать это – с помощью конструктора объекта: let x = new Object().

Примечание: Двойные квадратные скобки в [[Prototype]] означают, что свойство является внутренним и не может быть доступно непосредственно в коде.

Чтобы найти свойство [[Prototype]] этого нового объекта, нужно использовать метод getPrototypeOf ().

Вывод будет состоять из нескольких встроенных свойств и методов.

Еще один способ найти [[Prototype]] – это свойство __proto__, которое предоставляет внутренний [[Prototype]] объекта.

Это вернет такой же результат, что и getPrototypeOf().

Важно, чтобы каждый объект JavaScript имел [[Prototype]], поскольку он позволяет связать два и более объекта.

Созданные вами объекты имеют [[Prototype]] так же, как и встроенные объекты, такие как Date и Array. Сослаться на это внутреннее свойство можно с помощью свойства prototype.

Наследование прототипов

Когда вы пытаетесь получить доступ к свойству или методу объекта, JavaScript сначала выполняет поиск по самому объекту, и если искомое не найдено, он будет искать объект [[Prototype]]. Если после поиска по объекту и его [[Prototype]] совпадения не найдено, JavaScript проверит прототип связанного объекта и продолжит поиск до тех пор, пока не достигнет конца цепочки прототипов.

В конце цепочки прототипов находится Object.prototype. Все объекты наследуют свойства и методы Object. Любая попытка поиска за пределами цепочки приводит к null.

В нашем примере x – пустой объект, который наследуется от Object. x может использовать любое свойство или метод, которые имеет Object, например toString().

x.toString();
[object Object]

Давайте рассмотрим другой тип объекта. Если у вас есть опыт работы с массивами JavaScript, вы знаете, что у них много встроенных методов (таких как pop() и push()). У вас есть доступ к этим методам при создании нового массива потому, что любой массив, который вы создаете, имеет доступ к свойствам и методам Array.prototype.

Создайте новый массив:

Помните, что создать его можно также с помощью конструктора массива: let y = new Array().

Если посмотреть на [[Prototype]] нового массива y, вы увидите, что он имеет больше свойств и методов, чем объект x. Он унаследовал все это от Array.prototype.

y.__proto__;
[constructor: ƒ, concat: ƒ, pop: ƒ, push: ƒ, …]

Вы увидите свойство constructor в прототипе, для которого задано значение Array(). Свойство constructor возвращает функцию-конструктор объекта, которая является механизмом для построения объектов из функций.

Эта цепочка теперь относится к Object.prototype. Можно проверить внутренний [[Prototype]] на свойство prototype функции конструктора, чтобы увидеть, что они ссылаются на одно и то же.

y.__proto__ === Array.prototype; // true
y.__proto__.__proto__ === Object.prototype; // true

Также для этого можно использовать свойство isPrototypeOf():

Array.prototype.isPrototypeOf(y); // true
Object.prototype.isPrototypeOf(Array); // true

Можно использовать оператор instanceof, чтобы проверить, появляется ли свойство prototype конструктора в пределах цепочки прототипов объекта.

y instanceof Array; // true

Итак, все объекты JavaScript имеют скрытое внутреннее свойство [[Prototype]] (которое можно определить с помощью __proto__ в некоторых браузерах). Объекты могут быть расширены и наследуют свойства и методы от [[Prototype]] их конструктора.

Прототипы складываются в цепочки, и каждый дополнительный объект наследует все по этой цепочке. Цепочка заканчивается на Object.prototype.

Функции-конструкторы

Функции-конструкторы – это функции, которые используются для построения новых объектов. Оператор new используется для создания новых экземпляров на основе функции конструктора. Вы уже знаете некоторые встроенные конструкторы JavaScript (new Array() и new Date(), например); вы также можете создавать собственные пользовательские шаблоны для построения объектов.

Предположим, что вы создаете очень простую текстовую ролевую игру. Пользователь может выбрать персонажа, а затем класс персонажа (например, воин, целитель, вор и т. д.).

Поскольку каждый персонаж будет иметь множество характеристик – имя, уровень, количество набранных баллов – имеет смысл создать конструктор. Однако, поскольку каждый класс персонажа может иметь совершенно разные способности, нужно, чтобы каждый персонаж имел доступ только к своим способностям. Давайте попробуем добиться этого с помощью наследования прототипов и конструкторов.

Функция-конструктор изначально является обычной функцией. Она становится конструктором, когда экземпляр вызывает ее с ключевым словом new. По соглашению JavaScript функция-конструктор записывается с большой буквы.

// Initialize a constructor function for a new Hero
function Hero(name, level) <
this.name = name;
this.level = level;
>

Теперь у вас есть функция-конструктор Hero с двумя параметрами: name и level. Поскольку у каждого персонажа будет имя и уровень, для них имеет смысл наследовать эти свойства. Ключевое слово this будет ссылаться на новый созданный экземпляр; this.name в параметре name гарантирует, что новый объект будет иметь свойство name.

Создайте новый экземпляр с помощью new.

let hero1 = new Hero(‘Bjorn’, 1);

Если запросить в консоли hero1, вы увидите новый объект с правильно установленными свойствами:

Теперь, если запросить [[Prototype]] объекта hero1, вы увидите constructor Hero().

Object.getPrototypeOf(hero1);
constructor: ƒ Hero(name, level)

Как видите, пока что в конструкторе определены только свойства, а не методы. В JavaScript методы прототипов обычно определяются для повышения эффективности и удобочитаемости кода.

Мы можем добавить помощью prototype. Создайте метод greet().

// Add greet method to the Hero prototype
Hero.prototype.greet = function () <
return `$ says hello.`;
>

Поскольку greet() – это prototype в Hero, а hero1 является экземпляром Hero, метод будет доступен и для hero1:

hero1.greet();
«Bjorn says hello.»

Если вы проверите [[Prototype]] в Hero, вы увидите доступную опцию greet().

Теперь нужно создать классы персонажей. Вкладывать все способности для каждого класса в конструктор Hero не имеет смысла, потому что разные классы будут иметь разные способности. Нужно создать новые функции-конструкторы и связать их с оригинальным Hero.

С помощью метода call() скопируйте свойства одного конструктора в другой. Создайте конструкторы Warrior и Healer.

.
// Initialize Warrior constructor
function Warrior(name, level, weapon) <
// Chain constructor with call
Hero.call(this, name, level);
// Add a new property
this.weapon = weapon;
>
// Initialize Healer constructor
function Healer(name, level, spell) <
Hero.call(this, name, level);
this.spell = spell;
>

Оба новых конструктора теперь обладают свойствами Hero и несколькими уникальными свойствами. Добавьте метод attack() в Warrior и метод heal() в Healer.

Теперь можно создать персонажей с двумя новыми доступными классами:

const hero1 = new Warrior(‘Bjorn’, 1, ‘axe’);
const hero2 = new Healer(‘Kanin’, 1, ‘cure’);

Теперь hero1 распознается как Warrior с новыми свойствами.

Можно использовать новые методы, установленные в прототипе Warrior.

hero1.attack();
«Bjorn attacks with the axe.»

Но что произойдет, если попробовать использовать следующие методы в цепочке прототипов?

hero1.greet();
Uncaught TypeError: hero1.greet is not a function

Свойства и методы прототипа не связываются автоматически, когда вы используете call() для создания цепочек. Используйте Object.create(), чтобы связать прототипы, прежде чем создавать и добавлять какие-либо дополнительные методы к прототипу.

.
Warrior.prototype = Object.create(Hero.prototype);
Healer.prototype = Object.create(Hero.prototype);
// All other prototype methods added below
.

Теперь можно использовать методы прототипа из Hero в экземплярах Warrior или Healer.

Вот полный код страницы создания персонажа.

В этом файле вы создали класс Hero с базовыми свойствами, два класса персонажей – Warrior и Healer – из исходного конструктора, добавили методы в прототипы и создали отдельные экземпляры персонажей.

Заключение

JavaScript – это язык, основанный на прототипах, и он функционирует иначе, чем традиционная парадигма на основе классов, используемая многими другими объектно-ориентированными языками.

В этом мануале вы узнали, как работают прототипы JavaScript и как связать свойства и методы объекта с помощью скрытого свойства [[Prototype]], которым обладают все объекты. Также вы теперь умеете создавать пользовательские функции-конструкторы и использовать наследование прототипов для передачи значений свойств и методов.

Источник

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

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