Pull to refresh

Comments 25

Мне казалось сей холивар уже некоторое количество лет неактуален.

Меня забавляет тот факт, что классы в JS появились именно в тот момент, когда общий тренд повернулся к игнорированию ООП.


А насчет синтаксического сахара, так наверное всегда говорили про новые фишки, начиная с перехода с бинарного кода к ассемблеру.

тот момент, когда общий тренд повернулся к игнорированию ООП.

Что характерно, совпавший с моментом, когда в профессию набежали выпускники курсов "по хэтээмэлю за 21 день".

Меня больше забавляет, что в языке огромное количество давно известных тяжелых проблем, но консорциум добавляет такие невероятно важные вещи, как синтаксис классов (в функционально-ориентированном языке, да), потому что людям с джавой головного мозга без оных трудно было его усвоить, а тяжелые проблемы так и остаются нерешенными. Насколько мне известно, до сих пор в стандарте не закреплен синтаксис проверки цепочек свойств (восклицательный знак), хотя реализации в библиотеках были еще начиная с КофеСкрипта. Это как пример элементарной вещи, которая всем нужна и которую было бы проще простого внедрить, но веселее же заниматься ерундой, которую будет использовать 1% разработчиков. Это глобальная проблема современного фронтенда — колоссальная оторванность разработчиков стандартов и фреймворков от реальных кейсов их использования. Консорциум состоит из высококлассных разрабов, которые пишут на чем угодно, но только не на JS; Ден Абрамов с гордостью заявляет, что не умеет верстать и вообще, не знает очень многих элементарных фронтовых вещей. А потом мы удивляемся страданиям.

Классы в основном в серверном коде используются (Node), там без них очень плохо было бы. На клиенте меньше. Если вы делаете только клиент, то неудивительно, что удивляетесь, зачем нужны классы.

Не согласен.
У большой тройки на фронте: Angular, Vue, React есть способы описания через классы.
У первого причем только через классы. Vue, начал с классов, но в итоге тоже внедряет React-like api.

А по бэку, у ноды, я за все пакеты не могу сказать, но из того что я видел — везде процедурный стиль: тот же самый express

Тут какая штука… не скажу за другие, но в Реакте классы изначально были страшными костылями, и хуки стали реально прорывом. (Хотя хуки сами по себе тоже весьма костыльны и протекающи как абстракция, просто менее, чем классы.)


Так же, как за PrototypeJS стоял jQuery, за jQuery стоял Реакт с классами, а за классами стояли хуки. Так же, как за callback hell стояли промисы, а за промисами стоял async-await. Точно так же и за хуками стоит что-то еще (скорее всего, нечто напоминающее подход в Svelte, но не сам Svelte; Реакт следующий прорыв уже, думаю, не переживет, слишком много легаси и слишком стар Дэн Абрамов.). Это эволюция парадигм. Как эволюция физики и математики. Чертовски небыстрая штука, но в конце, глядя назад, думаешь «но почему до этого не догадались раньше?».

Есть же опциональная последовательность — optional chaining

Для UI как раз и ближе ООП, а вот бек ближе к процедурным, во всяком случае webapi. И вместо того чтобы немного посидеть и подумать, как раскидать модель, большой процент фронтэнд разработчиков начинают пилить лапшу из if/switch, да ещё и используя any где не попадя. В результате код не поддерживаемый, даже IDE пасует. Убил бы )))

Кто это игнорирует ооп кроме абрамова и его поклонников?

Хорошая статья, все по делу.


Про приватные #-свойства хотелось бы только добавить. С их использованием есть опасность (по крайней мере, с транспиляторами через WeakMap): объект получается принципиально неклонируемым снаружи. Вернее, при попытке склонировать через Object.create (да хоть как), получается новый объект, у которого все приватные свойства «битые», это можно слишком поздно заметить.

в более современном механизме, в классах
Классы современнее прототипов? Oh my!..
А транспилятор при этом, кроме того, должен выполнять проверку instanceof, что приводит к появлению более медленного кода, перегруженного служебными конструкциями.
Используйте компайл-тайм проверки, и вам не придётся проверять это в рантайме ни быстро, ни медленно. Раз уж всё равно используете транспилятор, то грех не возложить на него максимум проверок.
Предлагаю не обращать внимания на тот факт, что я даже не использую в конструкторе Array.apply(this, arguments), так как это тоже не приведёт к желаемому эффекту.
Возможно, потому, что функция-конструктор вообще не обязана обращаться к this? Потому что это не конструктор класса, а функция-конструктор, задача которой — сформировать объект с нужными свойствами. И писать её так, чтобы ей пользовались для посторонних целей, никто не обязан.
«Да кому вообще может понадобиться расширять String, дружище?». И это — правильный вопрос. Вам, вполне возможно, это и не понадобится. Но смысл тут не в том, надо это кому-то или нет, а в том, что сделать это средствами ES5 просто невозможно. Механизмам прототипного наследования это недоступно, а вот JS-классы способны на такие вещи.
По этой логике текущие версии языка хуже старых, потому что в них убрали возможность переопределять «undefined» и прочие ключевые слова. Потому что «смысл тут не в том, надо это кому-то или нет, а в том, что сделать это средствами ES7 просто невозможно».
окажется, что ES5 просто не создан для работы с тем, для чего используется Symbol.species. В ES5 в этом плане всё устроено очень неудобно и ненадёжно.
Опять же, расширять стандартные классы типы в JS не приветствуется, поэтому оно и неудобно.
// 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»)?
Это, блин, вообще, серьёзный вопрос? Мы вызываем чистую фунцию, которая никак не обращается к this, и ожидаем, что она что-то с этим this сделает? Это задачка для вводного урока по программированию для старшеклассников или что?
А теперь давайте взглянем на то, как это реализовано в JS-классах:
// ES6+
class Button {
  constructor() {
    return document.createElement('button');
  }
}
class MyButton extends Button {
  constructor(value) {
    super();
    this.textContent = value;
  }
}
Поздравляю, ваш «отличный и предсказуемый» пример только что незаметно для вас затёр в конструкторе «унаследованного» класса всё его содержимое, все его свойства и методы.
В ES5 в качестве конструктора может быть использована любая функция.
Может, потому что в ES5 есть только один вид функций, т.к. стрелочных тогда ещё не было? Вообще, старый стандарт — всегда хороший мальчик для битья. Легко сравнивать возможности старого стандарта с одной возможностью нового. Как будто весь функционал нового только и появился из-за классов. Причём тут стрелочные функции? При том, что их точно так же нельзя использовать как конструкторы. Безо всяких ручных рантайм-проверок.
При объявлении JS-классов можно пользоваться стрелочными функциями. То же самое справедливо и для ES5-конструкторов, но тут, чтобы это воспроизвести, как и в прочих подобных случаях, понадобится некоторый объём вспомогательного кода:
// ES5
function WithArrows() {
  Object.defineProperties(this, {
    method1: {
      configurable: true,
      writable: true,
      value: () => "arrow 1"
    }
  });
}
// ES6+
class WithArrows {
  method1 = () => "arrow 1";
}
// (new WithArrows).method1();
Во-первых, в ES5 просто не было стрелочных функций. Они появились только в ES6. Во-вторых, «но тут, чтобы это воспроизвести, как и в прочих подобных случаях, понадобится некоторый объём вспомогательного кода» и называется «синтаксический сахар». А в-третьих, объясните, что мешает просто написать конструктор точно так же в одну строчку?
function WithArrows() {
  this.method1 = () => "arrow 1";
}

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

что мешает просто написать конструктор точно так же в одну строчку?

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

И правда. Но это, конечно, какое-то весьма своеобразное наследование, если мы поле с сеттером из прототипа заменяем в объявлении класса на метод.

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

Да, я тоже могу придумать, зачем в прототипе для этого поля могут быть геттер и сеттер, но в классе-то предлагалось именно записать в поле функцию, без геттеров/сеттеров. Или что там произойдёт при наличии гет/сета — они каким-то образом тоже останутся, несмотря на переопределение свойства в объявлении класса?

Если именно переопредеять, а не просто сетить в ES5 - не останутся.

Ну, вот я и не вижу юз-кейса для этого. Какой-то манки-патчинг получается.
Use case — это про применение/пользу/смысл (use), а не про возможность так сделать. What's the use of all this? В чём смысл делать как в примере с классами, затирая оригинальное свойство с сеттером?
могу придумать, зачем в прототипе для этого поля могут быть геттер и сеттер, но в классе-то предлагалось именно записать в поле функцию, без геттеров/сеттеров.
не вижу юз-кейса для этого. Какой-то манки-патчинг получается.
По поводу конструкторов без new,
так сложилось, что иногда их вполне валидно можно вызвать, во всяком случае со стандартными типами
Number(’10’) === 10, что в будет плюс/минус эквивалентом
Number.call(undefined, '10’)

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

По поводу приватных полей и методов + WeakMap — жесть какая.
Инкапсуляция данных и методов, она практически всегда была реализована, как правильно упомянули выше в комментариях, через замыкания.
И она еще долго таковой и останется много где.

Посмотрите код любой либы, она с приличной вероятностью будет чем-то похожим на

(function closure() {…})()

что и есть инкапсуляция по сути своей.

Приватные методы и свойства всегда делались в es5 через замыкания, так что утверждение насчёт того что можно сделать их исключительно с weakmap не верно. Weakmap использовали только тогда когда можно было объявлять классы через директиву class но ещё не была введена поддержка приватных полей через #.

Sign up to leave a comment.