Сколько раз вы слышали о прототипном наследованииnheritance в Javascript?

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

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

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

Наследование ООП

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

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

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

Давайте взглянем на этот пример псевдокода и посмотрим, как будет выглядеть память, если код будет выполняться на каком-нибудь ООП-языке (например, Java или C#).

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

Объем памяти:

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

Таким образом, когда выполняется строка class B inherit A, на самом деле происходит то, что все свойства, которыми обладает определение класса A, будут скопированы в класс B. Позже, когда вы создаете экземпляр класса B, сконструированный объект будет содержать все, что определено в классе B, то есть все свойства класса A плюс специфические свойства класса B.

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

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

Здесь мы соединили объект B с объектом A. Итак, теперь объект А является прототипом объекта Б.

Большинство разработчиков JS скажут вам, что в этом случае объект B наследует все от объекта A.

И может возникнуть соблазн так думать, особенно из-за того, что когда вы вводите B.nameA, вы на самом деле возвращаете строковое значение 'Objectc A'. Но давайте взглянем на нашу память и посмотрим, что там.

Как мы видим, у объекта B есть только одно свойство (его собственное), и ничего не было скопировано из объекта A. Так является ли это наследованием? Можем ли мы сказать, что объект B 'унаследовал' свойства от объекта A? Думаю, нет. У объекта B есть связь с объектом A через прототип и способ запрашивать и получать свойства, которых у него нет. Такой «способ запроса» часто называют «алгоритмом поиска».

Прототип Наследование может сбивать с толку и вводить в заблуждение, особенно новых разработчиков. Когда вы слышите «Прототипное наследование в Javascript», очень легко запутаться, думая, что часть названия «Наследование» предполагает, что оно работает так же, как и в классических объектно-ориентированных языках. Как мы видели, это не так. Идея этих двух моделей различна, и, как мы видели, память выглядит совершенно по-разному.

Гораздо лучшим названием для этого было бы Prototypal Delegation! Потому что, когда какой-либо объект получает запрос на получение значения некоторого свойства, если у него нет этого свойства, он всегда будет делегировать задачу получения значения этого свойства своему прототипу.

Соображения производительности.

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

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

Вывод

Название Prototypal Inheritance используется в этом языке, вероятно, по той же причине, по которой язык называется JAVAscript ,и по той же причине у нас есть много ненужных синтаксических сахаров и дополнений к языку, таких как оператор instanceof, ключевое слово new (и весь конструктор шаблон вызова), класс ключевое слово и т. д. Все это может сбивать с толку и вводить в заблуждение новых разработчиков.

Моя цель здесь состояла в том, чтобы показать, насколько велика разница между наследованием ООП и прототипным наследованием javascript.

Основное отличие состоит в том, что в ООП наследование — это действие копирования. Один класс копирует все из какого-то другого (базового) класса. В JS у нас нет классов, и наследование — это не действие копирования, а действие делегирования. У нас есть средства для соединения двух объектов, чтобы один объект мог делегировать свои задачи (извлечение значения свойства) другому объекту.

Я надеюсь, что это поможет вам понять эти различия и устранить всю путаницу, связанную с этим модным словом в сообществе javascript.