Встроенные прототипы
Свойство «prototype» широко используется внутри самого языка JavaScript. Все встроенные функции-конструкторы используют его.
Сначала мы рассмотрим детали, а затем используем «prototype» для добавления встроенным объектам новой функциональности.
Object.prototype
Давайте выведем пустой объект:
Вот что происходит:
Когда вызывается new Object() (или создаётся объект с помощью литерала <. >), свойство [[Prototype]] этого объекта устанавливается на Object.prototype по правилам, которые мы обсуждали в предыдущей главе:
Мы можем проверить это так:
Обратите внимание, что по цепочке прототипов выше Object.prototype больше нет свойства [[Prototype]] :
Другие встроенные прототипы
Вот более полная картина (для трёх встроенных объектов):
Давайте проверим прототипы:
В браузерных инструментах, таких как консоль разработчика, можно посмотреть цепочку наследования (возможно, потребуется использовать console.dir для встроенных объектов):
Примитивы
Самое сложное происходит со строками, числами и булевыми значениями.
Специальные значения null и undefined стоят особняком. У них нет объектов-обёрток, так что методы и свойства им недоступны. Также у них нет соответствующих прототипов.
Изменение встроенных прототипов
В течение процесса разработки у нас могут возникнуть идеи о новых встроенных методах, которые нам хотелось бы иметь, и искушение добавить их во встроенные прототипы. Это плохая идея.
Так что, в общем, изменение встроенных прототипов считается плохой идеей.
В современном программировании есть только один случай, в котором одобряется изменение встроенных прототипов. Это создание полифилов.
Полифил – это термин, который означает эмуляцию метода, который существует в спецификации JavaScript, но ещё не поддерживается текущим движком JavaScript.
Тогда мы можем реализовать его сами и добавить во встроенный прототип.
Заимствование у прототипов
В главе Декораторы и переадресация вызова, call/apply мы говорили о заимствовании методов.
Это когда мы берём метод из одного объекта и копируем его в другой.
Некоторые методы встроенных прототипов часто одалживают.
Например, если мы создаём объект, похожий на массив (псевдомассив), мы можем скопировать некоторые методы из Array в этот объект.
Но это будет невозможно, если obj уже наследует от другого объекта. Помните, мы можем наследовать только от одного объекта одновременно.
Заимствование методов – гибкий способ, позволяющий смешивать функциональность разных объектов по необходимости.
Итого
Задачи
Добавить функциям метод «f.defer(ms)»
«Сложно о простом». Функции-конструкторы — как объекты,(prototype). Объекты,(__proto__). constructor, =<>, как функция-конструктор new Object()
В прошлый раз мы попытались разобраться со следующими вещами:
Функции-конструкторы, как объекты, prototype.
Начнём по порядку. Рассмотрим простую на первый взгляд строчку кода.
Самое первое что можно сказать: «Мы объявили функцию с именем A». Совершенно верно. Но здесь есть нюансы.
1. Не забываем что в JS — практически всё есть Объект. Функция, как оказалось не исключение(это даже два объекта связанные ссылкой).
2. Её можно использовать как функцию-конструктор.
В JavaScript нет того, что принято называть классами. Работу классов в JavaScript выполняют функции-конструкторы, которые создают объекты с определенными заданными свойствами.
В общем-то говоря любая объект-функция в JS может быть конструктором( я говорю о пользовательских функциях). Их условно можно поделить на три (DF(Функция декларация), FE(Функция выражение), функции созданные конструктором Function()). У всех этих функций есть свои особенности(по этому они разделены на разные группы), но о них я здесь рассказывать не буду, если кому интересно я отвечу лично или напишу отдельно про них в другой раз. Однако у них есть и одна общая черта, которая позволяет им быть конструкторами — это наличие внутренних свойств [[Construct]] и [[Call]], а также явного свойства prototype(о нем ниже).
Именно внутренний метод [[Construct]] отвечает за выделения памяти под новый объект и его инициализацию. Однако — это не значит что вызов функции приведёт к созданию объекта, конечно нет. Для этого перед вызовом функции нужно поставить оператор new. Именно new запускает метод [[Construct]] и связанные с ним процессы.
3. Так же можно сказать что это функция декларация(DF) и прочее, но остальное пока не важно.
Итак Функция «A» (из первой строчки первого примера) — это функция-конструктор и по совместительству объект. Раз это объект — она может иметь свойства. Так оно и есть. А раз это функций-конструктор, то она имеет свойство prototype. Свойство prototype — это ссылка на объект, который хранит свойства и методы которые перейдут к экземплярам созданным этой функцией-конструктором. Давайте попробуем всё это отобразить графически.
По умолчанию объект prototype «пустой» (ну почти пустой, но об это ниже). Выше я сказал что всё что лежит в этом объекте перейдёт в экземпляр, а так же будет доступно потомкам. То есть по умолчанию(если ничего в prototype не дописывать), то в экземпляр «ничего» не перейдёт от функции-конструктора «A». То есть при выполнении кода:
мы получим «обычный»( насколько это можно в JS ) объект «а».
В JS уже встроено много функций-конструкторов. Это например Number(), String() и т. д. Давайте отвлечёмся ненадолго от примера и поговорим о встроенных функциях-конструкторах и об Объектах в целом.
Объекты(__proto__).
Из прошлой статьи, мы знаем, что при создании (явно или не явно) объектов одним из встроенных конструкторов Number(), String() или Boolean(), экземпляр получает доступ к некоторым методам характерным данному типу. Например для Number() есть метод toPrecision(). Если посмотреть в консоли на объект созданный конструктором new Number(2), то Вы не обнаружите там этого метода(Вы вообще не обнаружете там методов). Откуда же он берётся? Как раз он и подобные ему методы(к которым должен иметь доступ потомок) и содержатся в prototype-объекте родителя. Но как экземпляр получает к ним доступ? У экземпляра есть свойство __proto__ — это ссылка на prototype-объект родителя. Если при вызове метода, метод не находится в самом экземпляре, происходит переход по ссылке __proto__ в prototype-объект родителя и поиск продолжается там. На самом деле так продолжается и дальше пока не будет встречен null.
Попробуем всё это нарисовать:
Подведя итог можно сказать, что пока всё не сложно: Есть родитель(функция-конструктор), у которой есть ссылка в свойстве prototype на некий объект где хранятся все методы и свойства к которым потомок должен иметь доступ. И есть, собственно, потомок которому при создании через вызов new от родителя передаётся ссылка в свойство __proto__ на тот самый объект с общими свойствами и методами.
Для закрепления попробуем рассмотреть пример:
constructor.
Я всегда брал слово (пустой) в кавычки когда говорил («пустой» prototype). Мол когда мы создаём функцию-конструктор function A()<>, то создаётся свойство prototype с ссылкой на «пустой» prototype-объект. На самом деле нет. В prototype всё же кое-что лежит. Во-первых поскольку как я уже говорил prototype — это «простой» Объект, то там лежит свойство __proto__ с ссылкой на prototype функции-конструктора Object() (именно она создаёт всё «простые», самые элементарные объекты), а во-вторых там лежит свойство constructor. Свойство constructor туда добавляет интерпретатор, когда понимает что создаётся функция-конструктор а не просто объект Для начала давайте дополним наш первый рисунок с учётом этих двух фактов.
Всё что нарисовано серым, нам сейчас особо не нужно — это для более полной картины. Сосредоточимся на свойстве constructor. Как видно из рисунка constructor указывает на саму функцию-конструктор для которой изначально было создано это «хранилище», этот объект. То есть между свойством prototype функции-конструктора и свойством constructor объекта-prototype появляется цикличность — они указывают на объекты друг-друга.
Через свойство constructor (если оно всё ещё указывает на конструктор, а свойство prototype конструктора, в свою очередь, всё ещё указывает на первоначальный прототип) косвенно можно получить ссылку на прототип объекта: a.constructor.prototype.x. А можно полуть ссылку к самой функции-конструктору и её свойствам которые были присвоены не в prototype-объект, а конкретно к ней. Например:
=<> — как функция-конструктор (new Object()).
Отлично, вроде как всё встало на свои места. Есть «общее хранилище», у родителя и потомка есть ссылки на это хранилище, если свойства нет в самом экземпляре, то интерпретатор перейдя по ссылке поищет его в «общем хранилище». В чём загвоздка?? Посмотрим Пример2:
Вроде как всё должно работать. Мы создали функцию-конструктор, задали «общему хранилищу» (prototype(через ссылку)) свойство (x), создали экземпляр, свойство (x) у него есть — всё нормально. Потом мы вообще переопределили свойство родителя prototype, добавив свойства (x) и (y) указали верный constructor. Всё должно работать в «общем хранилище лежит» оба этих свойства, но нет, (y) интерпретатор не находит. WTF.
Что же здесь за магия происходит? Почему мы не видим этих изменений из потомка этого конструктора? Почему потомок не видит y? Ну во-первых мы переопределяем свойство prototype функции-конструктора(B) и оно начинает ссылаться на новый объект (связь с первоначальным объектом prototype разорвана). Во-вторых обычное присвоение переменной объекта, типа: var a = <>, интерпретатором на самом деле выполняется как var a = new Object(). А это значит, что свойство prototype функции-конструктора теперь содержит совершенно новый объект у которого ссылка constructor отсутствует и чтоб не потерять родителя мы самостоятельно дописываем туда свойство constructor и присваиваем ему самого родителя.
А экземпляр сделанный ранее содержит ссылку __proto__ на старый объект prototype где свойства (y) нет. То есть в отличии от Примера1 здесь мы не «добавили в хранилище свойство» и даже не «переписали хранилище заново», мы просто создали новое, разорвав связь с старым, а экземпляр об этом ничего не знает, он всё ещё пользуется старым по своей старой ссылке __proto__. Выглядит это вот так:
Чёрным цветом — это то что не изменилось и после B.prototype =
Красным — то что удалилось
Зелёным — то что добавилось
Так же можно добавить немного об instanceof. Как ни странно, но в данном примере b1 будет принадлежать функции-конструктору B, а b — нет. Всё очень просто. Дело в том что instanceof ищет выполнения следующего условия — что бы объект указанный по ссылке __proto__(на любом уровне цепочки)(кружочек с цифрой 1) был равен объекту на который ссылается свойство prototype искомого родителя(кружочек с цифрой 2)(сравните на рисунке чёрный цвет и зелёный). В чёрном цвете это условие уже не выполняется, а в зелёном — выполняется.
В нашем случае у экземпляра (b) эта связь разорвана, так как новое свойство prototype искомого родителя(B) ссылается уже на новый объект, а не как раньше. Зато у экземпляра (b1) с этим как видим всё в порядке.
Вдогонку
По поводу this в теле функции-конструктора и вообще углубляться не буду — об этом в следующей статье. Единственное что скажу, это то, что this при вызове функции как конструктора(через new) будет указывать на создаваемый экземпляр, а при вызове как функции — на глобальный объект.
Давайте разберём на примере:
Как же узнать как вызвали функцию? Через new или нет? Это делается очень просто:
Примерно таким образом реализован механизм приведения типов. При выполнении например 1+’1′ интерпретатор воспринимает + как конкатенацию строк и пытается привести число 1 в строку. Это происходит с помощью неявного вызова String(1)(без new). А в конструкторе String написана примерно та же конструкция что у нас выше. То есть если вызов произошел без new просто вернуть строку(неявный вызов метода toString()). Таким образом без создания каких либо объектов происходит преобразование типов.
Так же хочу добавить следующее, что бы добавить свойство к функции(именно к функции а не к prototype) нужно обратится к ней как к объекту. Например
Это свойство будет недоступно потомку, так как оно не лежит в prototype, а потомок имеет доступ только туда. Но как говорится «если сильно хочется то можно». Тут то нам и пригодится свойсто объекта prototype — constructor. Оно как мы помним ссылается на саму функцию(если конечно этого специально не меняли). Тогда чтоб получить переменную val нужно обратится к ней так:
Прототипы в JS и малоизвестные факты
Лирическое вступление
Получив в очередной раз кучу вопросов про прототипы на очередном собеседовании, я понял, что слегка подзабыл тонкости работы прототипов, и решил освежить знания. Я наткнулся на кучу статей, которые были написаны либо по наитию автора, как он «чувствует» прототипы, либо статья была про отдельную часть темы и не давала полной картины происходящего.
Оказалось, что есть много неочевидных вещей из старых времён ES5 и даже ES6, о которых я не слышал. А еще оказалось, что вывод консоли браузера может не соответствовать действительности.
Что такое прототип
Объект в JS имеет собственные и унаследованные свойства, например, в этом коде:
Как выглядит прототип
Да кто такой этот ваш constructor
constructor – это ссылка на функцию, с помощью которой был создан объект:
Не совсем понятна идея зачем он был нужен, возможно, как способ клонирования объекта:
Но я не нашел подходящий пример его использования, если у Вас есть примеры проектов, где это использовалось, то напишите об этом. В остальном же использовать constructor лучше не стоит, так как это writable свойство, которое можно случайно перезаписать, работая с прототипом, и сломать часть логики.
Где живёт прототип
О чем вам недоговаривает дебаггер, или он вам не прототип
Свойство __proto__ является геттером и сеттером для внутреннего слота [[Prototype]] и находится в Object.prototype :
Из-за этого я избегал записи __proto__ для обозначения прототипа. __proto__ находится не в самом объекте, что приводит к неожиданным результатам. Для демонстрации попробуем через __proto__ удалить прототип объекта и затем восстановить его:
В консоли Chrome foo будет выглядеть следующим образом:
А теперь уберем связь между baz и Object.prototype :
И теперь в консоли Chrome видим следующий результат:
Как работать с прототипом объекта
Рассмотрим основные способы работы с прототипом: изменение прототипа и создание нового объекта с указанным прототипом.
А теперь менее категоричный вопрос создания нового объекта с прототипом. Для этого есть следующие способы.
Стандартный способ:
И в случае если отсутствует поддержка всего вышеперечисленного:
Функции и конструкторы
А теперь поговорим про функции и как они работают в качестве конструкторов.
Функция Person тут является конструктором и создает два поля в новом объекте, а цепочка прототипов выглядит так:
И теперь вызов user.fullName() вернет строку «John Doe».
Что такое new
На самом деле оператор new не таит в себе никакой магии. При вызове new выполняет несколько действий:
Все эти действия можно сделать силами самого языка, поэтому можно написать свой собственный оператор new в виде функции:
Но начиная с ES6 волшебство пришло и к new в виде свойства new.target, которое позволяет определить, была ли вызвана функция как конструктор с new, или как обычная функция:
new.target будет undefined для обычного вызова функции, и ссылкой на саму функцию в случае вызова через new ;
Наследование
Фиолетовым цветом обозначены поля объекта (они все находятся в самом объекте, т.к. this у всей цепочки прототипов один), а методы желтым (находятся в прототипах соответствующих функций)
Вариант 1 предпочтительнее, т.к. Object.setPrototypeOf может привести к проблемам с производительностью.
Сколько вам сахара к классу
Для того чтобы облегчить классическую схему наследование и предоставить более привычный синтаксис, были представлены классы, просто сравним код с примерами Person и Student:
Уменьшился не только бойлерплейт, но и поддерживаемость:
При этом цепочка прототипов получается идентичной примеру с явным указанием prototype у функций конструкторов.
Наивно было бы ожидать, что одна статья ответит на все вопросы. Если у Вас есть интересные вопросы, экскурсы в историю, аргументированные или беспочвенные заявления о том, что я сделал все не так, либо правки по ошибкам, пишите в комментарии.
P. P. S.
К сожалению главный кликбейт статьи перестал быть актуальным. В данный момент Chrome (версия 93, на момент обновления статьи) перестал использовать __proto__ для обозначения прототипа, и теперь отображает его как слот [[Prototype]] :
Справедливости ради хочу отметить что в Firefox (92) также не используется обозначение __proto__ :
Прототипы объектов
Базовая компьютерная грамотность, базовое понимание HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Строительные блоки) и основы OOJS (см. Введение в объекты).
Понять прототипы объектов JavaScript, как работают прототипные цепочки и как добавить новые методы в prototype свойство.
Язык основанный на прототипах?
JavaScript часто описывают как язык прототипного наследования — каждый объект, имеет объект-прототип, который выступает как шаблон, от которого объект наследует методы и свойства. Объект-прототип так же может иметь свой прототип и наследовать его свойства и методы и так далее. Это часто называется цепочкой прототипов и объясняет почему одним объектам доступны свойства и методы которые определены в других объектах.
Точнее, свойства и методы определяются в свойстве prototype функции-конструктора объектов, а не в самих объектах.
Давайте посмотрим на пример, чтобы стало понятнее.
Понимание прототипа объектов
В этом примере мы определили конструктору функцию, например:
Затем мы создаём экземпляр объекта следующим образом:
Если вы наберёте « person1. » в вашей консоли JavaScript, вы должны увидеть, что браузер пытается автоматически заполнить это с именами участников, доступных на этом объекте:
Свойство prototype: Где определены унаследованные экземпляры
Снова create()
Ранее мы показали, как метод Object.create() может использоваться для создания нового экземпляра объекта.
Свойство constructor
Это хорошо работает. Вам не нужно будет использовать его часто, но это может быть действительно полезно, если вы хотите создать новый экземпляр и не имеете ссылки на исходный конструктор, который легко доступен по какой-либо причине.
Свойство constructor имеет другие применения. Например, если у вас есть экземпляр объекта и вы хотите вернуть имя конструктора этого экземпляра, вы можете использовать следующее:
Например, попробуйте это:
Изменение прототипов
Давайте рассмотрим пример изменения свойства prototype функции-конструктора — методы, добавленные в прототип, затем доступны для всех экземпляров объектов, созданных из конструктора.
Должно появиться всплывающее окно, с именем пользователя, определённым в конструкторе. Это действительно полезно, но ещё более полезно то, что вся цепочка наследования обновляется динамически, автоматически делая этот новый метод доступным для всех экземпляров объектов, полученных из конструктора.
Подумайте об этом на мгновение. В нашем коде мы определяем конструктор, затем мы создаём экземпляр объекта из конструктора, затем добавляем новый метод к прототипу конструктора:
Примечание: Если у вас возникли проблемы с получением этого примера для работы, посмотрите на наш пример oojs-class-prototype.html (см. также это running live).
Это не очень гибко, так как человека нельзя назвать так. Было бы намного лучше сделать это, создав fullName из name.first и name.last :
Этот образец можно увидеть в действии в примере приложения плана школы Петра Залевы.
Резюме
В этой статье рассмотрены прототипы объектов JavaScript (в том числе и то, как прототип цепочки объектов позволяет объектам наследовать функции друг от друга), свойство прототипа и как его можно использовать для добавления методов к конструкторам и другие связанные с этой статьёй темы.
В следующей статье мы рассмотрим то, как вы можете реализовать наследование функциональности между двумя собственными настраиваемыми объектами.
JavaScript парадигма объектов и прототипов. Простое объяснение.
Многим новым разработчикам, особенно тем, кто привык работать с традиционным ООП, работать в мире JavaScript может показаться не удобно и не привычно. Для них код на JavaScript может выглядеть грязным и запутанным. Данная статья это попытка объяснить, максимально простым языком, что такое объекты в JavaScript и рассказать о механизме наследования на основе прототипов.
Хочу отметить, что я читал, множество статей, пытающихся объяснить что такое объекты в JavaScript, но я, никогда не читал те, которые объясняли бы их полностью или четко. Они часто сосредотачиваются исключительно на наследовании, оставляя без внимания другие важные моменты или не приводят достаточного количества примеров.
Что такое объекты?
Объекты на самом базовом уровне можно представить как список пар ключ / значение, причем ключ всегда является строкой, …а значение…. может быть чем-то иным. Это похоже на то, что вы можете назвать «картой» или «словарем» на других языках. Все, базовые сущности которые вы обычно создаете в JavaScript, и которое не является примитивами, является объектами. Объекты облегчают упаковку и перемещение данных, а создание новых объектов в JavaScript более тривиально, чем в других объектно-ориентированных, таких как Java / C #.
Когда говорят об объектах часто упоминают термин “свойство”. Этот термин означает определенную пару ключ/значение. Чтобы дать вам представление о том, как выглядят объекты, мы начнем с простого примера объекта с двумя свойствами: age и weight.
Это фрагмент кода демонстрирует использования литеральной (инициирующей) нотации объекта. Объектный литерал не является переменной или возвращаемым значением.
Функции являются объектами
Как было замечено раньше, в Javascript все что не примитивы являются объектами, включая функции… Я знаю это может показаться странным. Сложно думать о функции как о группе пар ключ/значение. Так как функции объекты, их часто еще называют как объекты-фукнции. Это специальные группа пар ключ/значение с особыми свойствами для выполнения кода и передачи значений. Мы рассмотрим эти свойства в следующем разделе. В начале давайте поговорим почему функции так важны.
Можно сказать, что объекты-функции имеют две основные цели. Если мы хотим создать блок логики, который выполняется, мы можем использовать объект-функцию: точно так же, как «методы» в любом другом языке программирования. Другой целью является то, что если мы захотим создать объекты со значениями и методами, и возможно, с некоторой логикой для установки этих значений, мы также будем использовать объекты-функции. Здесь вы можете думать о объектах-функциях как о «классах», которые ведут себя как объектно-ориентированные языки (то есть Java / C #).
В стандартном случае функции в JavaScript выглядят как функции на любом другом языке; они выполняют логику для выполнения конкретной задачи.
Если мы хотим упаковать небольшую группу данных, как, например, два наших свойства в объекте Dog, то достаточно простой список пар ключ / значение. Но что если мы захотим создать несколько объектов Dog? Может быть, некоторые значения должны быть статическими, а другие динамическими. В данном случае нам понадобятся объекты-функции. Когда мы вызываем функцию с помощью new, объект (он же экземпляр-объект) вернется со свойствами установленными с помощью ключевого слова this внутри функции.
Объекты против Прототипов
Теперь давайте поговорим о прототипах. Вы часто слышали, что JavaScript — это язык на основе прототипов. Значит ли это, что объекты и прототипы — это одно и то же? Ну, не совсем. Прототипы являются особым типом объекта и существуют как свойство для объектов-функций. Когда мы пытаемся получить доступ к ключу объекта-функции, JavaScript проверяет его свойство prototype, чтобы увидеть, есть ли оно там. Если нет, он пойдет вверх по цепочке прототипов, чтобы попытаться найти его. Чтобы понять цепочку прототипов, нам нужно узнать о функциях и наследовании.
Функции и наследование
Всякий раз, когда экземпляр объекта возвращается из вызова функции с использованием new, ему присваивается свойство с ключом __proto__. Значение этого свойства является свойство prototype функции, которая его создала.
Если мы попытаемся получить доступ к свойству объекта-экземпляра, а его там нет, JavaScript сначала перейдет к __proto__ и проверит, находится ли оно в прототипе родительской функции. Чтобы увидеть это в действии, давайте установим свойство в одном из атрибутов prototype нашего объекта Dog, и когда мы вызовем Spot[‘whatever the key name is’] или Bingo[‘whatever the key name is’], мы получим то же значение. Это будет работать даже после того, как будут созданы оба объекта-экземпляра dog.
Создание методов таким способом (в отличие от использования this внутри функций) особенно полезна, потому что создание метода будет происходить только один раз, а не каждый раз, когда вызывается new. Это будет экономить память и увеличит производительность.
Рассмотрим наследование немного глубже
Давайте рассмотрим несколько примеров JavaScript, движущихся вверх по цепочке прототипов для доступа к hasOwnProperty у некоторых объектов.
Литеральный объект (Object-literal):
Объект Экземпляр (Instance-Object):
Объект Функция (Function-object):
А как насчет __proto__ у объектов-функций?
Как уже говорилось, __proto__ помогает связать объекты с прототипами, от которых они наследуются. А как насчет вызова __proto__ непосредственно для объектов-функций? JavaScript действительно имеет встроенный объект-функцию под названием Function. Свойство __proto__ каждой функции указывает на Function.prototype, который является функцией, но не имеет свойства prototype и возвращает неопределенное значение. Function.prototype определяет поведение по умолчанию, от которого наследуются все функции. Как и все свойства прототипов функциональных объектов, он по-прежнему имеет __proto__, который указывает на Object.prototype.
Выше сказанное можно продемонстрировать следующей картинкой. Обратите внимание, что Object.prototype — это то, откуда все происходит.
Многоуровневое наследование
Когда мы говорим о наследовании, мы обычно думаем об объектах экземпляра, возвращаемых из функций. С прототипом вы также можете создать несколько уровней наследования и иметь объекты-функции, наследуемые от других объектов-функций. Все, что вам нужно сделать, это установить прототип дочернего объекта-функции в другой экземпляр прототипа родительского объекта-функции. Тогда все свойства родителя будут скопированы. Если родительская функция получает аргументы, такие как age и weight у Dog, используйте .call, чтобы установить свойство this дочернего объекта.
Labrador наследуется от Dog:
Классы
Классы в JavaScript, созданные в ES6, являются просто синтаксическим сахаром над объектами-функциями. Вместо того, чтобы набирать прототип снова и снова, чтобы определить методы для функций, с ключевым словом class мы можем просто определить группу методов внутри класса. С помощью ключевого слова extends классы могут наследоваться от других классов без необходимости использовать Object.create и Object.call. Лично мне больше нравится использовать классы, но имейте в виду, что старые браузеры могут их не поддерживать. Для решения этой проблемы есть такие инструменты, как Babel.
Использование объектов функций:
Аналогичный код но с использованием классов:
Объекты против примитивов
Код JavaScript по сути сводится к двум основным типам: примитивам и объектам. В JavaScript есть 5 примитивов: boolean, number, string, null и undefined. Примитивы — это всего лишь простые значения без свойств. Три примитива: boolean, number и string имеют дубликаты объектов, которые JavaScript использует как оболочку во время определенных операций. Например, «some string».length вызовет new String() и вернет объект-экземпляр, обернутый вокруг строкового примитива, чтобы можно было получить доступ к свойству length. Как уже упоминалось, все объекты-экземпляры наследуются от Object. Так что со строкой вы можете использовать методы родительского объекта, например тот же hasOwnProperty.







