Конструктор классов

Конструктор классов

Технический фундамент жизненного цикла экземпляра

При new SomeClass(...) происходит последовательность шагов:

  1. Создается пустой объект-экземпляр.
  2. Его прототип связывается с SomeClass.prototype.
  3. Вызывается constructor с переданными аргументами.
  4. Возвращается инициализированный экземпляр (если явно не возвращен другой объект).

Поэтому качество конструктора напрямую определяет корректность стартового состояния модели.

Смотри, что важно: если constructor явно возвращает объект, то результатом new станет этот объект (и он может не иметь методов класса). Возврат примитива (number/string/...) будет проигнорирован.

class A {
  constructor() {
    return { x: 1 };
  }
}

const a = new A();
console.log(a.x); // 1
console.log(a instanceof A); // false

Анти-провал: почти всегда constructor должен только инициализировать this и не возвращать другой объект вручную.

Зачем нужен constructor

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

Ключевой момент: constructor задает стартовое состояние экземпляра и фиксирует контракт входных данных.

Проверь себя: почему важно, чтобы экземпляр сразу после создания был в валидном состоянии?

Базовый синтаксис constructor

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
    this.isActive = true;
  }
}

const user = new User('Аня', 'anya@example.com');
console.log(user);

Что происходит:

  • new User(...) создает новый объект;
  • constructor получает аргументы;
  • this указывает на новый экземпляр;
  • свойства записываются в объект.

Здесь часто путаются: constructor не нужно вызывать вручную, он срабатывает автоматически.

Значения по умолчанию

Если часть данных опциональна, удобно задавать дефолты прямо в параметрах.

class Task {
  constructor(title, priority = 'normal') {
    this.title = title;
    this.priority = priority;
    this.done = false;
  }
}

console.log(new Task('Проверить PR'));

Смотри, что важно: дефолты уменьшают количество ошибок из-за undefined и делают API класса удобнее.

Валидация в конструкторе

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

class Product {
  constructor(name, price) {
    if (typeof name !== 'string' || name.trim() === '') {
      throw new Error('Некорректное имя товара');
    }

    if (typeof price !== 'number' || price < 0) {
      throw new Error('Некорректная цена');
    }

    this.name = name;
    this.price = price;
  }
}

Анти-провал: лучше не создать объект вообще, чем создать объект в плохом состоянии и ловить баги позже.

Проверь себя: почему стратегия fail fast особенно полезна внутри constructor?

Реальные микро-сценарии

  1. Модель пользователя при регистрации.

Конструктор нормализует и проверяет вход: имя, email, флаги статуса.

  1. Модель корзины.

Конструктор задает начальные поля (items, currency, createdAt) и гарантирует, что объект готов к работе сразу.

  1. UI-компонент.

Конструктор принимает конфиг и подготавливает состояние для рендера.

Частые ошибки новичков

  • Писать тяжелую бизнес-логику внутри constructor.
  • Забывать задавать важные поля и получать "полупустой" объект.
  • Не валидировать критичные входы.
  • Пытаться иметь несколько constructor в одном классе (в JS это нельзя).
class Example {
  constructor(a) {}
  // constructor(b) {} // SyntaxError: только один constructor
}

Дополнительный пример: конструктор с объектом параметров и дефолтами.

class Session {
  constructor({ userId, locale = 'ru', isGuest = false } = {}) {
    if (!userId && !isGuest) throw new Error('userId required for non-guest');
    this.userId = userId ?? null;
    this.locale = locale;
    this.isGuest = isGuest;
  }
}

Лучше держать constructor компактным: инициализация + базовая валидация.

Что будет, если изменить входные данные

В new Product('Книга', 500) объект создастся нормально. В new Product('', 500) сработает ошибка валидации имени. В new Product('Книга', -1) сработает ошибка по цене. Такое поведение предсказуемо и защищает от скрытых багов.

Проверь себя: что лучше для поддерживаемости - создать объект с price = -1 и чинить позже или бросить ошибку сразу?

Краткий итог

  • constructor автоматически запускается при создании экземпляра через new.
  • Его задача - корректно инициализировать стартовое состояние объекта.
  • Значения по умолчанию делают API класса удобнее и надежнее.
  • Валидация в constructor защищает систему от невалидных экземпляров.
  • Хороший constructor короткий, предсказуемый и без лишней бизнес-магии.