Классы: синтаксис

прототипы≈ 7 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Класс — это короткая запись для того, что раньше собирали вручную: функция-конструктор плюс методы в prototype. Синтаксис компактнее, правила строже — и в этом весь смысл.

Разделы темы:·

Класс описывает, как устроены объекты одного вида. Само объявление ничего не создаёт — это заготовка. Объекты появляются при вызове с new, и в этот момент срабатывает особый метод constructor: он получает аргументы и записывает начальные данные в this.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Конструктор не обязателен. Если его нет, движок подставит пустой, и new просто вернёт объект без собственных свойств. Конструктор в классе может быть только один — перегрузки, как в других языках, здесь нет.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Между методами в теле класса не ставят запятых. Это не объектный литерал, а отдельная грамматика — запятая там будет синтаксической ошибкой.

Метод пишется без слова function: имя, скобки, тело. Внутри this — тот объект, у которого метод вызвали. Важная деталь: сам метод в объект не попадает. В объекте лежат только данные, записанные конструктором, — к полям класса вернёмся ниже.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

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

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
разбор вывода
За move объект сходил в прототип: своего метода у него нет, поэтому p.hasOwnProperty("move") отвечает false. У Point.prototype метод собственный — true. Копия функции одна на все объекты класса.

Поле — это свойство, объявленное прямо в теле класса, без this и без конструктора. Оно записывается в каждый новый объект при создании, ещё до того, как выполнится тело конструктора. В отличие от методов, поля живут именно в объекте, а не в прототипе.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

У полей есть приятный побочный эффект. Если значением поля сделать стрелочную функцию, она навсегда запомнит this того объекта, в котором создалась. Такую «функцию-поле» можно оторвать от объекта и передать куда угодно — она не потеряет контекст, в отличие от обычного метода.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Слова get и set перед методом превращают его в свойство. Снаружи это выглядит как обычное чтение и присваивание, а внутри выполняется код: можно почистить данные, проверить их или посчитать на лету. Настоящее значение обычно хранят в отдельном свойстве — по традиции с подчёркиванием.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Сеттер можно не писать вовсе — тогда получится свойство только для чтения. Это удобно для значений, которые не хранятся, а вычисляются из других: площадь, полное имя, итоговая сумма. Присваивание такому свойству ничего не изменит, а в строгом режиме — бросит TypeError.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Никакой отдельной сущности «класс» в языке не появилось. Объявление class создаёт обычную функцию: её тело — это код конструктора, а все методы уходят в её prototype. Проверяется в одну строчку.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Почти то же самое можно собрать руками — так и писали до появления классов. Результат для того, кто пользуется объектом, неотличим: тот же new, тот же вызов метода, тот же typeof. Класс просто избавляет от ручной возни с prototype.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Раз класс — значение, его можно присвоить переменной, передать в функцию или вернуть из неё. Такая запись называется class expression, и имя после слова class в ней необязательно.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

«Синтаксический сахар» не значит «ровно то же самое». У классов есть отличия, и все они сделаны в одну сторону: язык не даёт выстрелить себе в ногу привычными способами.

ЧтоФункция-конструкторclass
Вызов без newРаботает, this — undefined или глобальный объектTypeError
Использование до объявленияОбъявление всплывает, вызов сработаетВременная мёртвая зона, ReferenceError
Режим кода внутриКак у окружающего кодаВсегда строгий
Методы в цикле for...inПеречисляютсяНе перечисляются

Класс нельзя вызвать как обычную функцию — забыть new и молча получить мусор не выйдет. Это не мелочь: раньше такая опечатка тихо портила глобальный объект, а теперь она заметна сразу.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

И объявление класса не всплывает так, как объявление функции. До строки с class имя уже занято, но обращаться к нему нельзя — это временная мёртвая зона, та же, что у let и const. Плюс всё внутри класса выполняется в строгом режиме, даже если в файле нет "use strict".

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

  • Класс — заготовка; объекты создаёт 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 и возвращает сам объект — тогда вызовы можно связывать в цепочку.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Площадь не хранится, а вычисляется геттером — значит, после scale она пересчитается сама. Чтобы работала цепочка, scale возвращает this.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Если бы площадь записывалась в обычное свойство в конструкторе, после scale она осталась бы старой. Геттер снимает эту проблему целиком.

Полное имя через геттер и сеттер

Класс User хранит firstName и lastName. Добавьте свойство fullName: при чтении оно собирает имя и фамилию через пробел, при записи — разбирает строку обратно на две части.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Подчёркивание здесь не нужно: настоящие данные уже лежат в двух обычных свойствах, а fullName — вычисляемая надстройка над ними.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Геттер и сеттер носят одно имя fullName и вместе описывают одно свойство.