- Писать класс с
constructorи методами и создавать экземпляры черезnew - Расширять класс через
extendsиsuperи переопределять методы - Использовать приватные поля
#, геттеры/сеттеры иstatic-методы
У банка тысячи клиентов, и все счета устроены одинаково: владелец, баланс, операции пополнения и снятия. Писать объект каждого счёта вручную долго, и легко ошибиться. Класс — это чертёж для объектов: структура описывается один раз, а потом по нему создаётся сколько угодно объектов.
Класс, конструктор и методы
Класс объявляют ключевым словом class, а его имя пишут с заглавной буквы. Особый метод constructor вызывается автоматически при создании каждого нового объекта и задаёт его свойства. this указывает на конкретный создаваемый объект. Объекты создают оператором new; объект, созданный по классу, называют экземпляром (instance).
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() вызывает родительскую версию.
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).
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
Собственный класс ошибки
Очень полезное применение наследования — собственные виды ошибок. Если расширить встроенный класс Error и задать name, в блоке catch можно точно определить вид ошибки через instanceof и легко отделить ожидаемые ошибки от неожиданных.
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.
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.