Перейти к содержанию
Educora
Продвинутый18 мин13 / 27

Классы и наследование

Создавай однотипные объекты с помощью классов: конструктор, методы, наследование через `extends`, приватные поля, геттеры и статические методы.

Проверь себя
В этом уроке ты узнаешь
  • Писать класс с constructor и методами и создавать экземпляры через new
  • Расширять класс через extends и super и переопределять методы
  • Использовать приватные поля #, геттеры/сеттеры и static-методы

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

Класс, конструктор и методы

Класс объявляют ключевым словом class, а его имя пишут с заглавной буквы. Особый метод constructor вызывается автоматически при создании каждого нового объекта и задаёт его свойства. this указывает на конкретный создаваемый объект. Объекты создают оператором new; объект, созданный по классу, называют экземпляром (instance).

JavaScript
class BankAccount {
  constructor(owner, balance = 0) {
    this.owner = owner;
    this.balance = balance;
  }

  deposit(amount) {
    this.balance += amount;
    return this.balance;
  }

  describe() {
    return `${this.owner}: ${this.balance} AZN`;
  }
}

const aysel = new BankAccount('Aysel', 100);
const murad = new BankAccount('Murad');

aysel.deposit(50);
console.log(aysel.describe());
console.log(murad.describe());
console.log(aysel instanceof BankAccount);
▸ Ожидаемый результат
Aysel: 150 AZN
Murad: 0 AZN
true
Методы у двух экземпляров общие, а данные — свои: операция со счётом Айсель не влияет на баланс Мурада.

Наследование: extends и super

Сберегательный счёт умеет всё, что обычный, и вдобавок приносит проценты. Чтобы не повторять код, новый класс наследуют от старого через extends: он получает все методы родителя. В конструкторе потомка сначала нужно вызвать super(...) — это запускает конструктор родителя. Метод с тем же именем переопределяет метод родителя, а super.describe() вызывает родительскую версию.

JavaScript
class BankAccount {
  constructor(owner, balance = 0) {
    this.owner = owner;
    this.balance = balance;
  }
  deposit(amount) { this.balance += amount; }
  describe() { return `${this.owner}: ${this.balance} AZN`; }
}

class SavingsAccount extends BankAccount {
  constructor(owner, balance, rate) {
    super(owner, balance);
    this.rate = rate;
  }
  addInterest() { this.deposit((this.balance * this.rate) / 100); }
  describe() { return `${super.describe()} (savings, ${this.rate}%)`; }
}

const leyla = new SavingsAccount('Leyla', 1000, 5);
leyla.addInterest();
console.log(leyla.describe());
console.log(leyla instanceof BankAccount);
▸ Ожидаемый результат
Leyla: 1050 AZN (savings, 5%)
true
Интерактив
Загрузка симуляции…
Если Лейла будет вызывать addInterest() раз в год, как вырастут её 1000 манатов за 10 лет? Меняй ставку и срок.

Приватные поля, геттеры и статические методы

  • Поле, начинающееся с #, приватное: к нему можно обратиться только внутри класса. Запись home.#celsius снаружи — синтаксическая ошибка.
  • Методы get и set читаются и записываются как свойства, но за ними работает код — например, проверка значения.
  • static-метод принадлежит самому классу, а не экземпляру, и вызывается так: Thermostat.fromFahrenheit(68).
JavaScript
class Thermostat {
  #celsius = 20;

  get celsius() { return this.#celsius; }
  set celsius(value) {
    if (value < 5 || value > 30) {
      throw new RangeError('Temperature must be between 5 and 30');
    }
    this.#celsius = value;
  }
  get fahrenheit() { return (this.#celsius * 9) / 5 + 32; }
  static fromFahrenheit(f) {
    const t = new Thermostat();
    t.celsius = Math.round(((f - 32) * 5) / 9);
    return t;
  }
}

const home = new Thermostat();
home.celsius = 25;
console.log(home.celsius, home.fahrenheit);
try { home.celsius = 40; } catch (error) { console.log(error.name, '-', error.message); }
console.log(Thermostat.fromFahrenheit(68).celsius);
▸ Ожидаемый результат
25 77
RangeError - Temperature must be between 5 and 30
20
Сеттер не пропускает неверное значение: 40 градусов нельзя, поэтому температура остаётся 25.

Собственный класс ошибки

Очень полезное применение наследования — собственные виды ошибок. Если расширить встроенный класс Error и задать name, в блоке catch можно точно определить вид ошибки через instanceof и легко отделить ожидаемые ошибки от неожиданных.

JavaScript
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

function register(age) {
  if (age < 13) {
    throw new ValidationError('Too young to register');
  }
  return 'Welcome!';
}

for (const age of [16, 10]) {
  try {
    console.log(register(age));
  } catch (error) {
    if (error instanceof ValidationError) {
      console.log(`${error.name}: ${error.message}`);
    } else {
      throw error;
    }
  }
}
▸ Ожидаемый результат
Welcome!
ValidationError: Too young to register
ТерминЗначение
classчертёж для объектов
newсоздаёт новый экземпляр класса
constructorзадаёт свойства нового экземпляра
extends / superнаследование и обращение к родителю
#fieldполе, видимое только внутри класса
staticметод класса, а не экземпляра
Задание

Допиши класс Rectangle: конструктор сохраняет width и height, а area() возвращает площадь. Затем напиши класс Square, наследующий Rectangle: его конструктор принимает одно значение side и вызывает super.

Задание · JavaScript
class Rectangle {
  constructor(width, height) {
    // save width and height
  }

  area() {
    // return the area
  }
}

class Square extends Rectangle {
  // constructor(side) → call super
}

const rect = new Rectangle(3, 4);
const square = new Square(5);
console.log(rect.area(), square.area());
console.log(square instanceof Rectangle);
console.log(square);
▸ Ожидаемый результат
12 25
true
Square { width: 5, height: 5 }

Главное

  • Класс — чертёж для объектов; экземпляры создают через new.
  • constructor задаёт свойства, а this указывает на конкретный экземпляр.
  • extends даёт наследование; в конструкторе потомка сначала вызывают super().
  • Поля # приватны, get/set контролируют свойство, static принадлежит самому классу.
  • Расширяя класс Error, можно создавать собственные виды ошибок.

Проверь себя

Вопросов: 10. Каждый правильный ответ приносит XP.

1 / 10
Каким оператором создают новый экземпляр класса?