Перейти к содержимому
Тема 15 из 16
Теория

Классы

Зачем это нужно

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

На классах построены многие библиотеки, а встроенные Map, Set, Date, Error — тоже классы.

Объявление класса

class User {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Привет, я ${this.name}`;
  }
}

const anya = new User("Аня", 15);
const timur = new User("Тимур", 17);
console.log(anya.greet());
console.log(timur.age);

Вывод:

Привет, я Аня
17
  • class User { … } — объявление класса. Имена классов пишут с большой буквы.
  • constructor — особый метод, который вызывается при создании объекта. В нём задают свойства.
  • this — создаваемый объект. this.name = name кладёт значение в его свойство name.
  • new User(…) — создать экземпляр. Без new будет ошибка.
  • Методы (greet) пишутся внутри класса без слова function и без запятых между ними.

Методы меняют состояние

class Counter {
  constructor() {
    this.value = 0;
  }
  increase(step = 1) {
    this.value += step;
    return this.value;
  }
}

const c = new Counter();
c.increase();
c.increase(5);
console.log(c.value);

Вывод:

6

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

Геттеры и сеттеры

Геттер — метод, который читается как свойство, без скобок. Сеттер — метод, который срабатывает при присваивании.

class Temperature {
  constructor(celsius) {
    this.celsius = celsius;
  }
  get fahrenheit() {
    return this.celsius * 9 / 5 + 32;
  }
  set fahrenheit(f) {
    this.celsius = (f - 32) * 5 / 9;
  }
}

const t = new Temperature(25);
console.log(t.fahrenheit);
t.fahrenheit = 212;
console.log(t.celsius);

Вывод:

77
100

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

Приватные поля

Поле с # в начале имени видно только внутри класса:

class Wallet {
  #money = 0;
  add(x) { this.#money += x; }
  get total() { return this.#money; }
}
const w = new Wallet();
w.add(50);
console.log(w.total);

Вывод:

50

Снаружи w.#money — синтаксическая ошибка. Так защищают данные от случайного изменения. По старинке «внутренние» поля помечают подчёркиванием (this._money) — это просто договорённость.

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

Класс может наследовать другой: получить все его свойства и методы и добавить свои.

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} издаёт звук`;
  }
  sleep() {
    return `${this.name} спит`;
  }
}

class Cat extends Animal {
  speak() {
    return `${this.name} мяукает`;
  }
}

const murka = new Cat("Мурка");
console.log(murka.speak());   // свой метод
console.log(murka.sleep());   // унаследованный
console.log(murka instanceof Cat, murka instanceof Animal);

Вывод:

Мурка мяукает
Мурка спит
true true

Если у дочернего класса свой constructor, в нём первым делом нужно вызвать super(...) — конструктор родителя:

class Student extends Animal {
  constructor(name, school) {
    super(name);          // родитель запишет this.name
    this.school = school;
  }
  speak() {
    return super.speak() + " и отвечает урок";  // вызов метода родителя
  }
}
console.log(new Student("Тимур", "№5").speak());

Вывод:

Тимур издаёт звук и отвечает урок

Статические методы

static — метод самого класса, а не экземпляров: Math.max устроен похоже.

class MathUtil {
  static square(x) { return x * x; }
}
console.log(MathUtil.square(7));

Вывод:

49

Как это работает внутри

Методы класса не копируются в каждый объект — они лежат в одном общем месте, прототипе (User.prototype). Когда ты вызываешь anya.greet(), JS ищет greet сначала в самом объекте, не находит и идёт в прототип. При наследовании прототипы выстраиваются в цепочку: Cat.prototype → Animal.prototype → Object.prototype. Поиск идёт по цепочке, пока метод не найдётся. Поэтому murka.sleep() работает, хотя в Cat его нет.

Частые ошибки

  1. Без new. User("Аня") → TypeError: Class constructor User cannot be invoked without 'new'.
  2. Без this. name = name в конструкторе ничего не сохраняет в объект. Нужно this.name = name.
  3. Забыли super(). В дочернем конструкторе обращение к this до super(...) — ошибка.
  4. Запятые между методами. В классе методы пишутся без запятых (в отличие от объекта).
  5. Геттер со скобками. acc.balance() → ошибка «is not a function». Геттер читают без скобок: acc.balance.

Шпаргалка

Что Как
Класс class Name { constructor(a) { this.a = a; } }
Экземпляр const x = new Name(1);
Метод go() { return this.a; }
Геттер / сеттер get total() {…}, set total(v) {…}
Приватное поле #secret = 0;
Наследование class B extends A { … }
Родитель super(…) в конструкторе, super.method()
Статический метод static create() {…}
Проверка x instanceof Name

Прямоугольник

Лёгкая+10 XP

Напиши класс Rectangle:

  • конструктор принимает width и height и сохраняет их в одноимённые свойства;
  • метод area() возвращает площадь;
  • метод perimeter() возвращает периметр.
const r = new Rectangle(3, 4);
r.area()       → 12
r.perimeter()  → 14

Подключи коннектор, чтобы решать задачи

Код задач выполняется на твоём компьютере — так быстрее и надёжнее, чем в браузере. Для этого нужна бесплатная программа-коннектор CodeWithMe. Подключить её нужно один раз.

Ищем коннектор на этом компьютере…

  1. 1
    Скачай коннектор для своей системы (Windows, macOS или Linux).Скачать коннекторНа странице загрузки выбери файл со своей системой в названии, например windows-amd64.
  2. 2Запусти его и выбери папку, где будут лежать твои решения. Python и Node.js он поставит туда сам, когда понадобится.
  3. 3
    Введи ниже код из 6 цифр — он написан в окне коннектора («Код для сайта CodeWithMe»). Если браузер спросит про доступ к локальной сети — разреши.

Коннектор открытый (исходный код на GitHub), работает только на твоём компьютере и выполняет код лишь после ввода кода сопряжения.