Как работают классы в JavaScript

Hydrock
Front Stories
Published in
3 min readJun 4, 2018

Вдохновение How JavaScript Classes Work от Thon Ly

Введение

Введение классов (Classes) в JavaScript должно было сделать создание объектов более очевидным. Но на самом деле, классы в js — это всего лишь синтаксический сахар, и это может ввести в заблуждение, особенно людей из более «традиционного» программирования. Под капотом, классы в JS являются объектами. Классов в «классическом» смысле не существует.

Например, класс JavaScript для создания экземпляра автомобиля теперь имеет вот такой простой синтаксис:

После создания, объект будет иметь свойства maker, model и метод drive.

Чтобы создать класс Tesla, наследуемый от класса Car, делаем так:

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

Constructor / Конструктор

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

Когда мы вызываем функцию с ключевым оператором new, создается новый, пустой объект. В данном случае, this, ссылаться на вновь созданный объект. Отрабатывает код функции, и объект возвращается.

Methods / Методы

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

Тут стоит напомнить о прототипах и наследовании. Как только вы создали/определили функцию, так же создается специальный объект который будет находиться по ссылке имя_функции.prototype. После создания экземпляра объекта, у него будет ссылка __proto__ который ссылается на тот самый прототип.

То есть имя_функции.prototype === экземпляр.__proto__

Итак, делаем метод:

Именно ключевой оператор new задает свойство __proto__ у вновь созданного объекта ссылкой на свойство prototype функции конструктора. Таким вот образом, возможен вызов метода объекта, даже если он не находится в самом объекте. Движок не найдя метод в объекте, будет продолжать искать его в цепочке прототипов __proto__, даже если придется пройти путь до самого корневого элемента.

Inheritance / Наследование

Более того, JavaScript использует цепочку прототипов для создания наследования. Чтобы класс Tesla наследовался от класса Car, мы определяем функцию конструктор Tesla, и вызываем функцию конструктор Car с текущим контекстом, указывающим на новый объект от Tesla. Это эквивалентно вызову super().

И еще раз, подробнее. В момент вызова Tesla с new — создается экземпляр — объект. this ссылается на этот объект. Далее вызывается конструктор Car посредством явного указания контекста call, и так как this ссылается на вновь созданные объект, все свойства конструктора Car применяются именно к новому объекту.

Но этого еще недостаточно. Необходимо связать прототип конструктора Tesla c прототипом конструктора Car. Используем Object.create(). Мы создадим у конструктора Tesla новый прототип который в свою очередь будет ссылаться (__proto__) на прототип конструктора Car.

Также необходимо восстановить свойство constructor указывающий на функцию конструктор, так как при создании прототипа мы затерли значение созданное при объявлении функции. Используем метод defineProperty для объявления свойства constructor, с дескриптором, свойства которого содержит “enumerable: false”, так как изначально свойство не является перечисляемым.

Наконец, мы можем определить метод charge() на прототипе конструктора Tesla.

Итак, создали экземпляр Tesla. Объект будет иметь свойства созданные конструктором Car и Tesla. При вызове метода drive(), движок не найдя его в объекте и прототипе объекта пойдет по цепочке __proto__ и найдет нужный метод в прототипе Car. Метод charge() уже находится в прототипе Tesla.

Заключение

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

Я надеюсь, вам понравился пост. Если да, похлопайте 👏, чтобы помочь другим найти эту информацию. И не стесняйтесь оставлять комментарии — буду рад любым замечаниям!

--

--