Comments 25
Меня забавляет тот факт, что классы в JS появились именно в тот момент, когда общий тренд повернулся к игнорированию ООП.
А насчет синтаксического сахара, так наверное всегда говорили про новые фишки, начиная с перехода с бинарного кода к ассемблеру.
тот момент, когда общий тренд повернулся к игнорированию ООП.
Что характерно, совпавший с моментом, когда в профессию набежали выпускники курсов "по хэтээмэлю за 21 день".
Классы в основном в серверном коде используются (Node), там без них очень плохо было бы. На клиенте меньше. Если вы делаете только клиент, то неудивительно, что удивляетесь, зачем нужны классы.
У большой тройки на фронте: Angular, Vue, React есть способы описания через классы.
У первого причем только через классы. Vue, начал с классов, но в итоге тоже внедряет React-like api.
А по бэку, у ноды, я за все пакеты не могу сказать, но из того что я видел — везде процедурный стиль: тот же самый express
Тут какая штука… не скажу за другие, но в Реакте классы изначально были страшными костылями, и хуки стали реально прорывом. (Хотя хуки сами по себе тоже весьма костыльны и протекающи как абстракция, просто менее, чем классы.)
Так же, как за PrototypeJS стоял jQuery, за jQuery стоял Реакт с классами, а за классами стояли хуки. Так же, как за callback hell стояли промисы, а за промисами стоял async-await. Точно так же и за хуками стоит что-то еще (скорее всего, нечто напоминающее подход в Svelte, но не сам Svelte; Реакт следующий прорыв уже, думаю, не переживет, слишком много легаси и слишком стар Дэн Абрамов.). Это эволюция парадигм. Как эволюция физики и математики. Чертовски небыстрая штука, но в конце, глядя назад, думаешь «но почему до этого не догадались раньше?».
Поддержку опциональной последовательности в браузеры давненько подвезли, но в стандарте да, только как пропозиция, даже не черновик
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Есть же опциональная последовательность — optional chaining
Кто это игнорирует ооп кроме абрамова и его поклонников?
Del
Хорошая статья, все по делу.
Про приватные #-свойства хотелось бы только добавить. С их использованием есть опасность (по крайней мере, с транспиляторами через WeakMap): объект получается принципиально неклонируемым снаружи. Вернее, при попытке склонировать через Object.create (да хоть как), получается новый объект, у которого все приватные свойства «битые», это можно слишком поздно заметить.
в более современном механизме, в классахКлассы современнее прототипов? Oh my!..
А транспилятор при этом, кроме того, должен выполнять проверку instanceof, что приводит к появлению более медленного кода, перегруженного служебными конструкциями.Используйте компайл-тайм проверки, и вам не придётся проверять это в рантайме ни быстро, ни медленно. Раз уж всё равно используете транспилятор, то грех не возложить на него максимум проверок.
Предлагаю не обращать внимания на тот факт, что я даже не использую в конструкторе Array.apply(this, arguments), так как это тоже не приведёт к желаемому эффекту.Возможно, потому, что функция-конструктор вообще не обязана обращаться к this? Потому что это не конструктор класса, а функция-конструктор, задача которой — сформировать объект с нужными свойствами. И писать её так, чтобы ей пользовались для посторонних целей, никто не обязан.
«Да кому вообще может понадобиться расширять String, дружище?». И это — правильный вопрос. Вам, вполне возможно, это и не понадобится. Но смысл тут не в том, надо это кому-то или нет, а в том, что сделать это средствами ES5 просто невозможно. Механизмам прототипного наследования это недоступно, а вот JS-классы способны на такие вещи.По этой логике текущие версии языка хуже старых, потому что в них убрали возможность переопределять «undefined» и прочие ключевые слова. Потому что «смысл тут не в том, надо это кому-то или нет, а в том, что сделать это средствами ES7 просто невозможно».
окажется, что ES5 просто не создан для работы с тем, для чего используется Symbol.species. В ES5 в этом плане всё устроено очень неудобно и ненадёжно.Опять же, расширять стандартные
Это, блин, вообще, серьёзный вопрос? Мы вызываем чистую фунцию, которая никак не обращается к this, и ожидаем, что она что-то с этим this сделает? Это задачка для вводного урока по программированию для старшеклассников или что?// ES5 function Button() { return document.createElement('button'); } function MyButton(value) { Button.call(this); this.textContent = value; } Object.setPrototypeOf(MyButton, Button); Object.setPrototypeOf(MyButton.prototype, Button.prototype);
Как думаете, что случится после вызова new MyButton(«content»)?
А теперь давайте взглянем на то, как это реализовано в JS-классах:Поздравляю, ваш «отличный и предсказуемый» пример только что незаметно для вас затёр в конструкторе «унаследованного» класса всё его содержимое, все его свойства и методы.
// ES6+ class Button { constructor() { return document.createElement('button'); } } class MyButton extends Button { constructor(value) { super(); this.textContent = value; } }
В ES5 в качестве конструктора может быть использована любая функция.Может, потому что в ES5 есть только один вид функций, т.к. стрелочных тогда ещё не было? Вообще, старый стандарт — всегда хороший мальчик для битья. Легко сравнивать возможности старого стандарта с одной возможностью нового. Как будто весь функционал нового только и появился из-за классов. Причём тут стрелочные функции? При том, что их точно так же нельзя использовать как конструкторы. Безо всяких ручных рантайм-проверок.
При объявлении JS-классов можно пользоваться стрелочными функциями. То же самое справедливо и для ES5-конструкторов, но тут, чтобы это воспроизвести, как и в прочих подобных случаях, понадобится некоторый объём вспомогательного кода:Во-первых, в ES5 просто не было стрелочных функций. Они появились только в ES6. Во-вторых, «но тут, чтобы это воспроизвести, как и в прочих подобных случаях, понадобится некоторый объём вспомогательного кода» и называется «синтаксический сахар». А в-третьих, объясните, что мешает просто написать конструктор точно так же в одну строчку?
// ES5 function WithArrows() { Object.defineProperties(this, { method1: { configurable: true, writable: true, value: () => "arrow 1" } }); } // ES6+ class WithArrows { method1 = () => "arrow 1"; } // (new WithArrows).method1();
function WithArrows() {
this.method1 = () => "arrow 1";
}
Вывод: автор просто совершенно не понимает, как работают конструкторы в JS, что, однако, не мешает ему с апломбом критиковать базовые конструкции языка.
что мешает просто написать конструктор точно так же в одну строчку?
Вызовется сеттер, если он объявлен в прототипе для этого поля. Нативные классы в этом случае сеттер не вызывают.
В данном случае же не метод, а свойство хранящее ссылку на функцию. Может там геттер, который эту функцию собирает билдером или компилирует из строки. И сеттер, позволяющий её реактивно менять.
Если именно переопредеять, а не просто сетить в ES5 - не останутся.
Я же выше привёл юз-кейс.
могу придумать, зачем в прототипе для этого поля могут быть геттер и сеттер, но в классе-то предлагалось именно записать в поле функцию, без геттеров/сеттеров.
не вижу юз-кейса для этого. Какой-то манки-патчинг получается.
так сложилось, что иногда их вполне валидно можно вызвать, во всяком случае со стандартными типами
Number(’10’) === 10, что в будет плюс/минус эквивалентом
Number.call(undefined, '10’)
И вообще непонятно зачем так делать?
Мы же не делаем всяких глупостей, просто потому что можем.
У вас же есть код ревью?
По поводу приватных полей и методов + WeakMap — жесть какая.
Инкапсуляция данных и методов, она практически всегда была реализована, как правильно упомянули выше в комментариях, через замыкания.
И она еще долго таковой и останется много где.
Посмотрите код любой либы, она с приличной вероятностью будет чем-то похожим на
(function closure() {…})()
что и есть инкапсуляция по сути своей.
Приватные методы и свойства всегда делались в es5 через замыкания, так что утверждение насчёт того что можно сделать их исключительно с weakmap не верно. Weakmap использовали только тогда когда можно было объявлять классы через директиву class но ещё не была введена поддержка приватных полей через #.
Известный символ Symbol.species
Если я правильно помню, его убирают из стандарта.
https://github.com/tc39/proposal-rm-builtin-subclassing
https://docs.google.com/document/u/0/d/1YDfpZDKEhiPWjcr4RPIpU3ZuHsccDg0k7QpKQBQKsLE/mobilebasic
JavaScript-классы — это не просто «синтаксический сахар»