Классы JS — это не «просто синтаксический сахар»

От автора: прочитав очередное сообщение в блоге о том, что классы JS являются «просто сахаром для прототипного наследования», я решил написать этот пост, чтобы еще раз прояснить, почему это утверждение вводит в заблуждение; пост, который, надеюсь, объясняет, в чем разница и почему важно ее понимать.

Использование use strict

Использование команды use strict в ES5 не будет запрещать вызывать конструкторы без ключевого слова new.

Причина в том, что современные классы имеют концепцию new.target, которую иначе невозможно воспроизвести в ES5 без использования транспиляторов, имитирующих такое поведение. Транспилятор также должен использовать проверку instanceof, что приводит к более медленному и раздутому коду.

Расширение встроенных функций

Вопреки моим экспериментам с подклассами Arrays, не представляется возможным расширять встроенные функции в ES5.

Давайте проигнорируем тот факт, что я даже не использую Array.apply(this, arguments) в конструкторе, так как это также не оправдает ожиданий, расширение массива в ES5 невозможно, также, как и всех остальных встроенных функций, включая String или другие.

Хорошо, Вы можете сказать: «Кому нужно расширять String?” И вы правы: возможно, вам это не нужно, но дело в том, что это невозможно сделать с ES5: прототипное наследование не может этого сделать, а классы JS могут.

Разновидности

Если вам интересно: «Почему list.slice(0) это не экземпляр List? ”, Ответ — Symbol.species.

ES5 здесь просто ненадежный и не предназначен для работы с видами. Вот и все: классы JS намного лучше сохраняют ожидания, чем ES5.

Super()

Если вам интересно, почему конструктор Array.apply(this, arguments) не работает в ES5? Ответ таков:

Array создает новый массив, он вообще не заботится о контексте, как и другие встроенные функции

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

Как вы думаете, что будет, когда будет вызван new MyButton(«content»)?

Возвращается кнопка с текстом Value

Возвращается экземпляр MyButton с полем textContent

И да, правильный ответ — последний, так что мы можем написать все подклассы как таковые:

Наши ожидания не оправдаются … и что плохого в таком подходе?

если суперкласс возвращает что-то еще, мы теряем наследование

если суперкласс является встроенным, мы могли бы иметь self, указывающий на примитив

Итак, вот еще один вариант, который исправляет первую проблемму, но не вторую:

Теперь посмотрим, как работают классы JS:

Еще раз: стоит ли писать такой код? Зависит от. Можем ли мы сказать, что классы JS намного лучше и мощнее ES5? Да!

Методы

Строго говоря, это не какая-то особенность классов JS, но это то, о чем многие не знают: методы не могут быть сконструированы, как сокращенные литеральные методы.

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

Перечисления

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

Стрелочные функции (=>)

В классах JS мы можем указывать стрелочные функции в определении класса. Мы можем сделать то же самое в конструкторах ES5, но нам снова понадобится много шаблонов для имитации того же:

Приватность

В классах JS у нас есть приватные поля, а с недавних пор и приватные методы.

Можем ли мы смоделировать приватные поля в ES5? Не совсем, если только мы не используем транспилятор и не WeakMap чтоб обернуть каждый экземпляр отдельными частями, которые никогда не должны быть доступны снаружи.

Заключение

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

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

Однако в таком случае правильное утверждение было бы больше похоже на: «Мне не нравятся классы JS, поэтому я думаю, что нужно использовать прототипное наследование».

Это гораздо более честное утверждение… хотя говорить, что классы JS — это «просто сахар», это очень плохое представление о современном JS и его возможностях.

Автор: Andrea Giammarchi

Источник: webreflection.medium.com

Редакция: Команда webformyself.

Читайте нас в Telegram, VK, Яндекс.Дзен

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии запрещены.