Классы JavaScript: конструкторы, методы, static

Что такое класс в JavaScript
Класс — это шаблон (blueprint) для создания объектов с общими свойствами и методами. Внутри класса вы обычно определяете конструктор и методы. В ES6 классы были добавлены как более привычный синтаксис для разработчиков, привыкших к объектно-ориентированному стилю (например, Java или C++), но под капотом они всё ещё работают через прототипы.
Определение в одну строку: класс — именованная конструкция для создания объектов с предопределёнными полями и поведением.
Важно: ключевое слово class обязательно для объявления класса. Без него код, написанный в стиле классов, не исполнится как класс.
Общая структура класса
Минимальная форма класса выглядит так:
class ClassName {
// тело класса
}Тело класса может содержать:
- конструктор: инициализирует состояние экземпляра;
- методы экземпляра: работают с this и данными конкретного объекта;
- статические методы: вызываются на самом классе, без экземпляра;
- поля класса (публичные/приватные — современные возможности JS).
Конструктор: зачем и как
Конструктор — это специальный метод с именем constructor. Он вызывается автоматически при создании нового объекта через оператор new. Конструктор задаёт начальные значения полей экземпляра.
Пример конструктора:
class Student {
constructor(firstName, lastName, startDate) {
this.firstName = firstName;
this.lastName = lastName;
this.startDate = startDate;
}
}Ключевые моменты:
- this указывает на создаваемый экземпляр класса;
- в JS нет обязательной декларации полей вне конструктора (в отличие от статически типизированных языков);
- если конструктор не задан, движок создаст пустой конструктор автоматически.
Совет: используйте const для переменных, которым не требуется переназначение, например const john = new Student(…).
Создание экземпляра
Чтобы создать объект (экземпляр класса), используйте new:
const john = new Student('John', 'Brown', 2018);new выполняет три шага в упрощённом виде:
- Создаёт пустой объект.
- Привязывает объект к прототипу класса.
- Вызывает constructor, где this указывает на вновь созданный объект.
Без переменной, на которую ссылается экземпляр, вы не сможете использовать его методы и свойства позже.
Методы экземпляра
Метод — это функция, объявленная внутри класса, которая оперирует данными конкретного объекта.
Пример метода report:
class Student {
constructor(firstName, lastName, startDate) {
this.firstName = firstName;
this.lastName = lastName;
this.startDate = startDate;
}
report() {
return `${this.firstName} ${this.lastName} started attending this institution in ${this.startDate}`;
}
}Вызов метода:
const john = new Student('John', 'Brown', 2018);
let result = john.report();
console.log(result);
// Выведет: John Brown started attending this institution in 2018Заметки по методам:
- Методы экземпляра имеют доступ к this.
- Не используйте стрелочные функции для методов, если нужен this, привязанный к экземпляру: стрелочная функция захватывает лексическое this и может вести себя не так, как ожидается.
Статические методы
Статические методы объявляются с ключевым словом static. Их можно вызывать напрямую на классе, без создания экземпляра.
Пример:
class Student {
constructor(firstName, lastName, startDate) {
this.firstName = firstName;
this.lastName = lastName;
this.startDate = startDate;
}
report() {
return `${this.firstName} ${this.lastName} started attending this institution in ${this.startDate}`;
}
static endDate(startDate) {
return startDate + 4;
}
}
console.log(Student.endDate(2018)); // 2022Статические методы полезны для фабричных функций, утилит и логики, не зависящей от конкретного состояния экземпляра.
Современные расширения классов
В современных версиях JavaScript доступны дополнительные возможности для классов:
- публичные поля: можно объявлять поля вне конструктора;
- приватные поля: синтаксис с #, например #password;
- статические поля;
- геттеры и сеттеры через get/set.
Пример приватного поля:
class User {
#password;
constructor(name, password) {
this.name = name;
this.#password = password;
}
checkPassword(value) {
return value === this.#password;
}
}Важно: приватные поля недоступны извне и не входят в прототип.
Когда классы не подходят — альтернативы
Классы удобны, но не всегда оптимальны. Рассмотрите альтернативы:
- Фабричные функции (factory functions). Просты и гибки.
function createStudent(firstName, lastName, startDate) {
return {
firstName,
lastName,
startDate,
report() {
return `${this.firstName} ${this.lastName} started in ${this.startDate}`;
}
};
}Композиция вместо наследования (compose over inherit). Вместо длинной иерархии классов собирайте объекты из независимых частей.
Прототипное наследование напрямую, если нужно тонко управлять прототипами.
Когда классы плохо работают:
- когда нужно лёгкое объединение поведения (mixins могут вызывать сложности);
- при необходимости низкоуровневого контроля цепочки прототипов;
- в функционально-ориентированных кодовых базах, где предпочтительна иммутабельность.
Частые ошибки и подводные камни
- Использование стрелочной функции для метода, когда нужен собственный this.
- Переопределение прототипных методов неправильно — теряются контракты.
- Попытка вызвать метод экземпляра на классе и наоборот.
- Ожидание, что поля будут объявлены заранее: в JS поля обычно создают внутри конструктора или объявляют как публичные поля.
- Неправильная работа с приватными полями (#) при попытке доступиться к ним извне.
Тестирование и критерии приёмки
Критерии приёмки для простого класса Student:
- Конструктор корректно сохраняет firstName, lastName и startDate.
- Метод report возвращает строку с корректными данными.
- Статический метод endDate возвращает startDate + 4.
- Приватные поля (если используются) недоступны извне.
Пример тестов (простая схема):
- Создать экземпляр с известными значениями.
- Проверить поля экземпляра.
- Вызвать report и сравнить со строкой-ожиданием.
- Вызвать Student.endDate(2018) и проверить результат 2022.
Рольовые чек-листы
Для разработчика:
- Выбрал ли я класс или фабрику и почему?
- Описал ли конструктор все необходимые поля?
- Не использую ли я стрелочные функции там, где нужен this?
- Добавлены ли тесты и комментарии?
Для ревьювера:
- Соответствует ли API класса требованиям?
- Есть ли побочные эффекты в конструкторах?
- Правильно ли используются приватные поля?
- Есть ли автотесты с граничными значениями?
Для QA:
- Тесты на создание/удаление объектов.
- Тесты на корректность методов и статических утилит.
- Проверка поведения при некорректных входных данных.
Ментальные модели и эвристики
- Класс как строительный чертёж: одинаковая структура, разные экземпляры.
- this как «этот конкретный дом», построенный по чертежу.
- Static как инструмент класса, а не отдельного дома (например, калькулятор сроков строительства).
- Композиция лучше наследования, когда поведение трудно разделить по уровням.
Совместимость и миграция
- Классы появились в ES6 (2015). Поддержка современных браузеров есть, но для старых окружений используют транспайлеры (Babel) и полифилы.
- Если проект поддерживает старые среды — используйте транспиляцию или фабрики.
- Приватные поля (#) поддерживаются в современных движках; для совместимости используйте условные альтернативы (символы, замыкания).
Безопасность и защита данных
- Не храните в открытых полях чувствительные данные (пароли, токены). Используйте приватные поля или внешнее хранилище.
- Методы должны проверять входные данные и не доверять внешнему вводу.
- Следите за сериализацией объектов: JSON.stringify не включает приватные поля и методы.
Примеры расширенного использования
Ниже — дополнительные примеры шаблонов и паттернов.
Фабрика с приватными данными через замыкание:
function createCounter() {
let count = 0; // приватно
return {
increment() { return ++count; },
get() { return count; }
};
}
const c = createCounter();
console.log(c.increment()); // 1Класс с геттером и сеттером:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
get area() {
return this.width * this.height;
}
set size({ width, height }) {
this.width = width;
this.height = height;
}
}Mixins (комбинация поведения) — осторожно с конфликтами имён:
const CanEat = Base => class extends Base {
eat() { console.log('eating'); }
};
class Animal {}
class Dog extends CanEat(Animal) {}Руководство по внедрению класса (SOP)
- Сформулируйте ответственность класса — одна обязанность.
- Определите поля и публичный API (методы).
- Решите: нужны ли приватные поля/методы.
- Реализуйте конструктор и методы.
- Напишите unit-тесты на поведение и граничные случаи.
- Сделайте code review и проверку стиля.
- Документируйте использование класса.
Факты и ориентиры
- ES6 (включая синтаксис class) был принят в 2015 году.
- Статические методы вызываются на классе, а методы экземпляра — на объекте.
- Приватные поля помечаются символом # и недоступны извне.
Частые сценарии использования
- Моделирование предметной области (домены): User, Order, Product.
- Утилиты и фабрики: DateUtils как статический набор функций.
- Инкапсуляция состояния с методами для управления этим состоянием.
Примеры тест-кейсов и критерии приёмки
Тест-кейсы:
- Создание экземпляра с валидными данными и проверка полей.
- Вызов метода report и валидация строки-результата.
- Попытка доступиться к приватному полю — должно быть недоступно.
- Проверка статического метода на корректный результат.
Критерии приёмки:
- Все тесты зеленые.
- Код покрыт минимум на уровне, требуемом командой (например, 70% — параметр команды).
- Нет утечек памяти при массовом создании/удалении экземпляров.
Глоссарий (одной строкой)
- Класс: шаблон для создания объектов.
- Экземпляр: объект, созданный по классу.
- Конструктор: метод, инициализирующий экземпляр.
- this: ссылка на текущий экземпляр.
- Статический метод: функция на уровне класса.
Часто задаваемые вопросы
Нужно ли всегда использовать class?
Нет. Для простых объектов и функциональных паттернов фабрики часто проще и гибче.
Чем private-поля (#) отличаются от обычных полей?
#-поля полностью скрыты извне и не попадают в прототип; обычные поля доступны и перечисляемы.
Как обеспечить совместимость с устаревшими браузерами?
Используйте транспайлер (Babel) и настройку целевых окружений.
Краткое резюме
- Классы в JavaScript делают код более декларативным и знакомым для OOP-разработчиков.
- Под капотом классы всё ещё используют прототипы.
- Выбирайте между классами и фабриками, исходя из требований к модульности и совместимости.
- Покрывайте классы тестами и следуйте принципам единой ответственности.
Важно: практика и чтение исходного кода помогут понять тонкости поведения классов в реальных проектах.
Похожие материалы
Переход с Microsoft Office на WPS Office
Как изменить цвет текста с помощью CSS
CSS тени: box-shadow и text-shadow
Как встроить MP3 на сайт — HTML5, Google Drive, CMS
Начать сайт с HTML5 Boilerplate — быстрое руководство