Обновить

Комментарии 30

Все части класса собраны в одном блоке {}.

Наоборот же: отделена логика инициализации состояния объекта в конструкторе, а описание функций - вынесено отдельно.

перепишем наш пример с использованием класса

Не вижу использования свойства prototype в примере с функцией-конструктором, это ведь не эквивалентный код.

Геттеры и сеттеры (get/set)
Эти инструменты — приятный бонус, который мы получаем при создании объектов с помощью классов.

Бонус - это синтаксис, а сами аксессоры можно добавлять через Object.defineProperty.

ЕМНИП, get/set можно просто для методов в объектных литералах задавать, без классов

Ну что вы неудобные вопросы то задаете? Автор пост написал что бы за час получить плюсики от коллег и забыть, а что там с содержанием не так уж и важно))

Не знал что приватные поля уже давно имеют хорошую поддержку во всех браузерах!

Большинство в действительности пользуются TypeScript, а там есть красивое ключевое слово private

Жаль, что TS нет в рантайме, private прекрасно отстреливается при компиляции и получается обычный публичный атрибут

Как обсуждали в предыдущей статье автора, приватные поля можно было получить и раньше через замыкания (хотя и не стоит использовать такой подход, потому что это ломает оптимизации движков).

Вообще глубоко убежден, что private поля были ошибкой. На практике нередко случается, например, что вам нужно какое-то поле в классе из библиотеки, а разработчик библиотеки подумал, что он лучше всех знает ООП не подумал о такой возможности и сделал его приватным. Это сильно ухудшает гибкость для сторонних разработчиков. В большинстве языков это нерешаемая проблема, приходится делать форк и поддерживать его в актуальном состоянии. В TS например действительно кейворд private ни на что не влияет в рантайме, что позволяет обойти его через приведение типов, если очень-очень нужно. Чего не скажешь про "#name" поля в JS.

Вообще глубоко убеждён, что отказ от глобальных переменных был ошибкой. На практике нередко случается, что вам нужен доступ к какому-то состоянию или флагу в коде библиотеки или фреймворка, а автор решил, что глобальное состояние — это «антипаттерн», и упрятал всё за приватные скоупы, DI или замыкания, не предусмотрев расширяемости. В итоге гибкость для сторонних разработчиков сильно страдает.

/s

Согласен! Но у нас осталась последняя надежда где нет этой ошибки

Советую взглянуть на $mol, серьезно

Это сильно ухудшает гибкость для сторонних разработчиков.

Гибкость не бесплатна: "Червяк может изгибаться как угодно, но в отличие от человека не может стоять." (Конрад Лоренц). В контексте прорграммирования гибкость мешает понижать сложность программ, а именно сложность является главной проблемой разработки сколь-нибудь больших программ (см. например "Мифический человекомесяц"). Идея, лежащая в основе ООП - снизить сложность программы путем разбиения состояния программы на слабо связанные части - объекты. Согласно принципу инкапсуляции из ООП, объекты обладают своим состоянием, являющимся частью общего состояния программы, и взаимодействовать с этим состоянием может не любой код в любом месте программы, а только код методов объекта. Вот за этим и нужны частные (private) поля в объектах.
А в целом гибкость и сложность находятся в противоречии: чем выше гибкость, тем больше вариантов написать программу, то есть - тем больше ее сложность (потенциальная), которую приходится иметь в виду и при чтении кода, и при его написании.

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

ООП нужен лишь человеку, чтобы иерархия или глубина вызовов функций влезали в его контекстное окно из 12-15 одновременно располагаемых в голове сущностей. Сложность самой программы от этого не меняется. Любую можно свести на ассемблер. Самое важное что не ввели ни в одном таком языке - это нативная поддержка статического управления, позволяя кодогенераторам или скан-ботам выявлять структуры и управлять ими на этапе разработки, то есть не вручную править классы а соответствующей IDE/редактором по спецификации объектов. По сути все изменения именно в этом механизме. На Питоне по крайней мере что-то сдвинулось с этой точки PEP 729 например. На С++/Java это замороженный 2004 год, когда упёрлись тактовой частотой в потолок и больше одиночным вычислителем уже не подчеркнуть "быстрее, выше, сильнее". Оттого и появилась "компилируемая безопасность" в попытке реализовать эти 5%. В итоге имеем интерпретируемые JSON/XML на все случаи жизни, для виду приправленные ООП, который в контексте пакетной обработки и приведения типов выворачивается наизнанку.

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

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

PS Остальные ваши мысли - они, может, и интересные, но я не вижу их связи ни со статьей, ни с моим комментарием, а потому оставляю без обсуждения.

Классы топ, особенно для бизнес логики

Так как сохраняют все инварианты внутри себя ( правила бизнеса )

https://youtu.be/ByBzzsnBnAY?si=H41rfKUfV8e9jwd9 мне вот тут нравится как разобрано

А ещё прикольно что в $mol этим классам ещё и накручено кучу всего крутого, по типу локализации, офлайн режима и кучи батареек ещё

Как дмитрий обратил такое юное дарование в своего последователя?

Да я сам)

Класс внутри себя ничего не хранит - это абстракция. Хранит физическая ячейка памяти располагаемая в условном массиве, который где-то на листе программиста размечен как класс. Класс может знать о том что он вообще существует, если его ID есть ячейка памяти. Класс может знать что у него есть методы, если есть таблица методов, располагаемых статически или как указатели на функции. Класс знает что он-часть иерархии, если есть таблица виртуальных функций и опять-таки его ID в ней как в линейном массиве или часть switch-case в зависимости от типа объекта при динамическом поведении.
В этом случае действительно разнести класс как спецификацию данных и бизнес-логику (оперирует состоянием автоматовx_n и x_{n-1}), которая вообще может не знать какие объекты она использует а работает с их хешами, идентификаторами или правилами обработки типов данных. В этом случае под катом может быть любой язык программирования, так как это всё генерируется автоматически, тем же ИИ-агентом.
То что класс может быть в топе - то есть на вершине стека, можно над этим подумать, действительно, иерархия может быть обработана как стек при размещении туда наследуемых объектов.

Что то на сложном. В чём идея ?

Класс внутри себя ничего не хранит - это абстракция.

Правильно. А программист - это человек, который приучен работать с абстракциями, причем - на разных их уровнях (если чо, эту мысль я не сам придумал, а подсмотрел у некого Рубена Герра, главного редактора PC Magazine: Russian Edition, году примерно в 1991). На разных уровнях - потому что с абстракциями высокого уровня работать проще, но их область применения ограничена: про абстракции вне области их применимости говорят, что они имеют дыры или протекают (последнее выражение - калька с английского, но используется тоже часто).

Так вот, класс (точнее, в данном случае - его экземпляр) - это абстракция. Переменная (в классе она называется поле) тоже абстракция, но уровнем ниже. Ячейка памяти - тоже абстракция, но на ещё более низком уровне. Абстракция - потому что ни в какой конкретной электронной схеме ячейка памяти не локализована: она может физически в конкретный момент быть как в кэше , так и в оперативной памяти, а то и вообще в странице виртуальной памяти, выгруженной на диск/во флэш, но работает с ней прикладной программист одинаковым образом, пренебрегая этими деталями, пока эти детали не начинают мешать - например, приводить к недопустимо низкой производительности. С классами дело обстоит примерно так же: этой абстракцией можно пользоваться, но следует помнить, что она применима ограниченно.

PS Если вам показалось, что здесь имеет место быть диалектика, то вам не показалось. Но это не важно.

Это из разряда "я мыслю значит я существую", на самом деле абстракцией является и электрическая схема, так как физически это металлизация, последствия ионной имплантации и прочих планаризаций. И нет там никаких "0" и "1" а есть переход уровня напряжения. Да и уровень этот тоже абстрактный - измеряемое число. "Объективная реальность данная нам в ощущениях". Как раз речь идёт ширине окна этих абстракций, что есть определённый уровень когда человек уже не может вобрать в себя модель полного поведения, однако есть нюанс - это всякие О большое, которое может сделать прогноз исходя из основополагающих величин. Мне не нужно знать защищённый режим и механизмы кеширования, если есть грубо говоря нечто D=C/H, где D - запаздывание вычислений, C - размер кода, H - размер кеша, в определённых границах разумеется. То есть любая абстракция может быть сведена к математическим оценкам с точки зрения оценки быстродействия и принятия решений. А на этом уровне уже и выбирается парадигма, инструмент будь то ассемблер или даже VHDL чтобы сделать сопроцессор для ASIC-а, а может это будет такой хитрый алгоритм на обычном Питоне под Pentium Pro который превзойдёт эти все асики вместе взятые. Вот тут и начинается собственно синтез, который вычёркивает любые классы и оставляет только разметку данных и последовательность оптимальной по заданному критерию их обработки. Время таких синтезаторов собственно и подоспело, так как вмещают в себя гораздо большее окно чем человек. Это как оптимизирующий компилятор - на ассемблерном уровне весьма неплох, особенно для ветвлений, что же касается векторных расширений SIMD, там да, необходимо додумывать иногда вручную, так как важен глобальный контекст.

В js недоклассы. Нет защищенных полей. Без них бывает туго, если приватное пле в базовом классе надо использовать в наследнике.

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

Нет защищенных полей. Без них бывает туго, если приватное пле в базовом классе надо использовать в наследнике

Не согласен на счет «недоклассов».

В js, действительно, нет protected, но все же такое поведение можно получить, например так:

class A {
    #private = 1;
    
    get protected() {
        return this.#private;
    }
}

class B extends A {

}

console.log(new B().protected) // 1;

или еще такой вариант:

class A {
    #private = 1;
    
    static getProtectedValue(instance: A) {
        return instance.#private;
    }
    
    static setProtectedValue(instance: A, newValue: any) {
        instance.#private = newValue;
    }
}

class B extends A { };
const instance = new B();
console.log(A.getProtectedValue(instance)); // 1;

A.setProtectedValue(instance, 2);
console.log(A.getProtectedValue(instance)); // 2;

// Или так
class C extends A {
    doWork() {
        return A.getProtectedValue(this);
    }
}

console.log(new C().doWork()); // 1;

Более того, так как конструкторы также наследуют свойства, то можно и так:

// ... объявление класса А как и выше

class B extends A { };
const instance = new B();

// тут уже обращение через B
console.log(B.getProtectedValue(instance)); // 1;

B.setProtectedValue(instance, 2);
console.log(B.getProtectedValue(instance)); // 2;


class C extends A {
  doWork() {
    // а тут через C
    return C.getProtectedValue(this);
  }
}

console.log(new C().doWork()); // 1;

Увы, это все еще обычные public свойства, которые можно вызывать у экземпляра класса

Увы, это все еще обычные public свойства, которые можно вызывать у экземпляра класса

Во первых, используйте Typescript и не сможете. Во вторых, и что? Вы же сами пишете код и знаете зачем вам вызов того или иного метода у класса. Раньше было просто условное соглашение __methodName считается приватным и его лучше не вызывать из вне, а methodName публичный

Все-таки некоторые вещи, которые можно сделать с помощью class, нельзя сделать с помощью прототипов, так что это не совсем одно и то же.

С классами в JavaScript вышло неловко. В нулевых был большой спрос на ООП в JS. В каждой второй библиотеке был велосипед для наследования. Когда классы добавили, мейнстрим свернул к ФП и классы стали маргинальными.

Там проблема была искусственная - это попытка прикрутить ООП к пакетной обработке данных, летящих к интерпретатору. Иерархия и классы - железобетонно прибиты идентификаторами к архитектуре. Веб-поведение динамично, объекты могут мутировать до состояния байта. Тем более это для поведения форм - полуфабрикат, который связывается дополнительно с разметкой документа (декларативное представление), которая как-то должна уживаться со спецификацией. Поэтому он ушёл в чисто поведенческую нишу ФП для разметки, фактически став знаком "=" в эксцеле.

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

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

Но на бэкенде, особенно там, где сложная бизнес-логика, классам вполне подходящее место. Мне, например, с классами на бэкенде работать гораздо удобнее, чем без них. Код организовывать проще. А если взять такой фреймворк, как Nest.js, то там вообще все через классы.

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

Честно говоря, статья крайне посредственная.

Автор просто выложил кусок учебника по JS, выкинув оттуда более интересную и полезную для нашей аудитории информацию: про внутрянку, про прототипы, про класс как выражение, из следующего параграфа — про статические свойства...

https://learn.javascript.ru/class

Минусовать, к сожалению, не могу. Оставлю это другим членам Хабра, которые не являются сотрудниками Selectel ;)

Кстати... А где самое главное-то: Классы это дёготь или сахар? Синтаксис мы рассмотрели... И всё? А как должно быть? А как НЕ должно быть? Что плохого в классах, что хорошего? Какие решения были удачными, а какие лишь портят общую картину?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
slc.tl
Дата регистрации
Дата основания
Численность
1 001–5 000 человек
Местоположение
Россия
Представитель
Александр Шилов