Классы: синтаксис
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Класс — это короткая запись для того, что раньше собирали вручную: функция-конструктор плюс методы в prototype. Синтаксис компактнее, правила строже — и в этом весь смысл.
Класс описывает, как устроены объекты одного вида. Само объявление ничего не создаёт — это заготовка. Объекты появляются при вызове с new, и в этот момент срабатывает особый метод constructor: он получает аргументы и записывает начальные данные в this.
Конструктор не обязателен. Если его нет, движок подставит пустой, и new просто вернёт объект без собственных свойств. Конструктор в классе может быть только один — перегрузки, как в других языках, здесь нет.
Между методами в теле класса не ставят запятых. Это не объектный литерал, а отдельная грамматика — запятая там будет синтаксической ошибкой.
Метод пишется без слова function: имя, скобки, тело. Внутри this — тот объект, у которого метод вызвали. Важная деталь: сам метод в объект не попадает. В объекте лежат только данные, записанные конструктором, — к полям класса вернёмся ниже.
Методы складываются в Имя.prototype — по одной копии на весь класс. Тысяча объектов — всё та же одна функция, к которой они обращаются через прототипную цепочку.
move объект сходил в прототип: своего метода у него нет, поэтому p.hasOwnProperty("move") отвечает false. У Point.prototype метод собственный — true. Копия функции одна на все объекты класса.Поле — это свойство, объявленное прямо в теле класса, без this и без конструктора. Оно записывается в каждый новый объект при создании, ещё до того, как выполнится тело конструктора. В отличие от методов, поля живут именно в объекте, а не в прототипе.
У полей есть приятный побочный эффект. Если значением поля сделать стрелочную функцию, она навсегда запомнит this того объекта, в котором создалась. Такую «функцию-поле» можно оторвать от объекта и передать куда угодно — она не потеряет контекст, в отличие от обычного метода.
Слова get и set перед методом превращают его в свойство. Снаружи это выглядит как обычное чтение и присваивание, а внутри выполняется код: можно почистить данные, проверить их или посчитать на лету. Настоящее значение обычно хранят в отдельном свойстве — по традиции с подчёркиванием.
Сеттер можно не писать вовсе — тогда получится свойство только для чтения. Это удобно для значений, которые не хранятся, а вычисляются из других: площадь, полное имя, итоговая сумма. Присваивание такому свойству ничего не изменит, а в строгом режиме — бросит TypeError.
Никакой отдельной сущности «класс» в языке не появилось. Объявление class создаёт обычную функцию: её тело — это код конструктора, а все методы уходят в её prototype. Проверяется в одну строчку.
Почти то же самое можно собрать руками — так и писали до появления классов. Результат для того, кто пользуется объектом, неотличим: тот же new, тот же вызов метода, тот же typeof. Класс просто избавляет от ручной возни с prototype.
Раз класс — значение, его можно присвоить переменной, передать в функцию или вернуть из неё. Такая запись называется class expression, и имя после слова class в ней необязательно.
«Синтаксический сахар» не значит «ровно то же самое». У классов есть отличия, и все они сделаны в одну сторону: язык не даёт выстрелить себе в ногу привычными способами.
| Что | Функция-конструктор | class |
|---|---|---|
| Вызов без new | Работает, this — undefined или глобальный объект | TypeError |
| Использование до объявления | Объявление всплывает, вызов сработает | Временная мёртвая зона, ReferenceError |
| Режим кода внутри | Как у окружающего кода | Всегда строгий |
| Методы в цикле for...in | Перечисляются | Не перечисляются |
Класс нельзя вызвать как обычную функцию — забыть new и молча получить мусор не выйдет. Это не мелочь: раньше такая опечатка тихо портила глобальный объект, а теперь она заметна сразу.
И объявление класса не всплывает так, как объявление функции. До строки с class имя уже занято, но обращаться к нему нельзя — это временная мёртвая зона, та же, что у let и const. Плюс всё внутри класса выполняется в строгом режиме, даже если в файле нет "use strict".
- Класс — заготовка; объекты создаёт new, а начальные данные записывает constructor — он один на класс и не обязателен.
- Методы попадают в prototype и общие для всех объектов, а поля класса записываются в каждый объект при создании.
- Поле со стрелочной функцией навсегда привязывает this к своему объекту — такую функцию можно передавать отдельно от объекта.
- get и set делают из метода свойство: снаружи обычное чтение и присваивание, внутри — код с проверками и вычислениями.
- Класс — это функция с методами в prototype, но со строгими правилами: обязательный new, временная мёртвая зона и всегда строгий режим.
1/3Где хранится метод hello, объявленный в теле класса User?
2/3Что выведет console.log(typeof A), если выше объявлено class A {}?
3/3Что произойдёт при вызове класса без new: class A {}, а затем A()?
Прямоугольник с площадью и масштабом
Напишите класс Rectangle. Конструктор принимает ширину и высоту. Геттер area возвращает площадь. Метод scale(k) умножает обе стороны на k и возвращает сам объект — тогда вызовы можно связывать в цепочку.
Площадь не хранится, а вычисляется геттером — значит, после scale она пересчитается сама. Чтобы работала цепочка, scale возвращает this.
Если бы площадь записывалась в обычное свойство в конструкторе, после scale она осталась бы старой. Геттер снимает эту проблему целиком.
Полное имя через геттер и сеттер
Класс User хранит firstName и lastName. Добавьте свойство fullName: при чтении оно собирает имя и фамилию через пробел, при записи — разбирает строку обратно на две части.
Подчёркивание здесь не нужно: настоящие данные уже лежат в двух обычных свойствах, а fullName — вычисляемая надстройка над ними.
Геттер и сеттер носят одно имя fullName и вместе описывают одно свойство.