Классы (class)
До этого мы создавали объекты вручную — каждый раз заново писали все свойства и методы. Но что если нужны сотни одинаковых объектов? Классы решают эту проблему: они позволяют описать шаблон один раз и создавать по нему сколько угодно объектов через new.
🤔 Проблема: сто одинаковых объектов
Представьте: вы делаете систему для интернет-магазина. У вас тысячи пользователей. Каждый пользователь — объект с именем, email, балансом и несколькими методами. Создавать их вручную — безумие:
// БЕЗ класса — так нельзя масштабировать!
const user1 = { name: "Анна", email: "a@mail.ru", balance: 0, ... };
const user2 = { name: "Борис", email: "b@mail.ru", balance: 0, ... };
const user3 = { name: "Вера", email: "v@mail.ru", balance: 0, ... };
// ... ещё 997 таких же строк?
Это ужасно — дублирование кода, высокий риск ошибки. Класс решает задачу: описываешь структуру один раз, создаёшь любое количество экземпляров.
🏭 Аналогия: класс — это форма для печенья. Описываешь форму один раз — делаешь сколько угодно одинаково-фигурных печенек с разной начинкой (данными).
📐 Синтаксис класса: class, constructor, new
Вот как выглядит базовый класс:
class User {
constructor(name, email) {
this.name = name; // this — создаваемый объект
this.email = email;
this.balance = 0; // начальное значение по умолчанию
}
greet() {
console.log(`Привет, меня зовут ${this.name}!`);
}
getInfo() {
return `${this.name} (${this.email}), баланс: ${this.balance} ₽`;
}
}
// Создаём экземпляры через new
const anna = new User("Анна", "a@mail.ru");
const boris = new User("Борис", "b@mail.ru");
anna.greet(); // Привет, меня зовут Анна!
boris.greet(); // Привет, меня зовут Борис!
console.log(anna.getInfo()); // Анна (a@mail.ru), баланс: 0 ₽
Разберём каждую часть по очереди:
class User { ... }
Объявление класса. User — имя класса в PascalCase (каждое слово с большой буквы). Это договорённость: классы всегда именуются так.
constructor(name, email)
Специальный метод инициализации. Вызывается автоматически при new User(...). Здесь задают начальное состояние объекта через this.свойство = значение.
new User("Анна", "a@m.ru")
Создание нового экземпляра класса. new — обязательное слово. Без него User("Анна") вызовет ошибку или вернёт не то.
🔤 Именование: PascalCase для классов
Это важная договорённость, которую нужно соблюдать. В JavaScript:
- Переменные и функции —
camelCase:userName,getBalance,isActive - Классы —
PascalCase:User,BankAccount,ShoppingCart,UserProfile - Константы —
UPPER_SNAKE_CASE:MAX_RETRIES,API_URL
PascalCase сигнализирует: «это класс, используй new». Если видишь new Something() — значит, Something — класс.
🏦 Практика: класс BankAccount
Давайте напишем полноценный класс банковского счёта — он отлично иллюстрирует все концепции:
class BankAccount {
constructor(owner, initialBalance = 0) {
this.owner = owner;
this.balance = initialBalance;
this.transactions = []; // история операций
}
deposit(amount) {
if (amount <= 0) return console.log("Сумма должна быть больше 0");
this.balance += amount;
this.transactions.push({ type: "deposit", amount });
console.log(`Пополнено +${amount} ₽. Баланс: ${this.balance} ₽`);
}
withdraw(amount) {
if (amount <= 0) return console.log("Сумма должна быть больше 0");
if (amount > this.balance) return console.log("Недостаточно средств!");
this.balance -= amount;
this.transactions.push({ type: "withdraw", amount });
console.log(`Снято -${amount} ₽. Баланс: ${this.balance} ₽`);
}
printStatement() {
console.log(`=== Выписка: ${this.owner} ===`);
this.transactions.forEach(t => {
const sign = t.type === "deposit" ? "+" : "-";
console.log(` ${sign}${t.amount} ₽`);
});
console.log(`Итого: ${this.balance} ₽`);
}
}
const acc1 = new BankAccount("Иван Петров", 5000);
const acc2 = new BankAccount("Мария Сидорова"); // balance = 0
acc1.deposit(3000); // Пополнено +3000 ₽. Баланс: 8000 ₽
acc1.withdraw(1500); // Снято -1500 ₽. Баланс: 6500 ₽
acc1.withdraw(10000); // Недостаточно средств!
acc1.printStatement(); // Выписка с историей
// Объекты независимы — у каждого своё состояние
console.log(acc2.balance); // 0
Обратите внимание: acc1 и acc2 — это два независимых объекта. У каждого свои balance и transactions. Изменение одного не влияет на другой. Это одно из главных преимуществ классов.
🔵 Статические методы: принадлежат классу, не экземпляру
До сих пор все методы вызывались через экземпляр: acc1.deposit(). Но иногда нужен метод, который принадлежит самому классу, а не конкретному объекту. Для этого есть ключевое слово static.
🔵 Статический метод вызывается через сам класс: ClassName.method(), а не через экземпляр. Внутри него this — это сам класс, а не объект.
class MathHelper {
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
static square(n) {
return n * n;
}
}
// Вызываем через класс, без new!
console.log(MathHelper.add(5, 3)); // 8
console.log(MathHelper.multiply(4, 6)); // 24
console.log(MathHelper.square(7)); // 49
// Вот пример из реального JS:
// Math.abs(), Math.max(), Math.random() — всё это статические методы класса Math!
Статические методы часто используют для:
- Утилитных функций — как
Math.abs(),Math.max() - Фабричных методов — альтернативных способов создания объектов
- Валидации — проверка данных перед созданием экземпляра
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
// Статический метод — фабрика
static createFromString(str) {
const [name, email] = str.split("|");
return new User(name.trim(), email.trim());
}
// Статический метод — валидация
static isValidEmail(email) {
return email.includes("@") && email.includes(".");
}
}
const u = User.createFromString("Алексей | alex@mail.ru");
console.log(u.name); // Алексей
console.log(User.isValidEmail("test@mail.ru")); // true
console.log(User.isValidEmail("нет_собаки")); // false
🔒 Приватные поля: # (ES2022)
Раньше мы использовали _balance как условно-приватное поле. Но это лишь договорённость — технически ничто не мешало написать acc._balance = 1000000. В ES2022 появились настоящие приватные поля через символ #:
class BankAccount {
#balance = 0; // настоящее приватное поле — с # !
constructor(owner, initial = 0) {
this.owner = owner;
this.#balance = initial;
}
deposit(amount) {
if (amount > 0) this.#balance += amount;
}
get balance() {
return this.#balance; // читать можно через геттер
}
}
const acc = new BankAccount("Иван", 1000);
console.log(acc.balance); // 1000 (через геттер — OK)
// Попытка залезть снаружи:
console.log(acc.#balance); // SyntaxError — реально запрещено!
acc.#balance = 9999999; // SyntaxError — защита работает!
Приватные поля (#field) — это не просто договорённость, а настоящая защита на уровне языка. Снаружи класса к ним невозможно обратиться — синтаксическая ошибка. Внутри класса — пожалуйста.
⚠️ Поддержка # появилась в Node.js 12+ и современных браузерах. Если работаете с устаревшим окружением — используйте _-конвенцию.
🍭 class — это синтаксический сахар
Важное понимание: class в JavaScript — это не отдельный механизм, а красивая обёртка над старым механизмом прототипов. Движок JavaScript при виде class User { ... } «под капотом» делает то, что раньше писали вручную — через функции-конструкторы и prototype.
Вот доказательство — оба способа создают одно и то же:
// Старый способ (до ES6, 2015):
function User(name) {
this.name = name;
}
User.prototype.greet = function() {
console.log("Привет, " + this.name);
};
// Новый способ (ES6+):
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log("Привет, " + this.name);
}
}
// Оба варианта работают одинаково!
Прототипы мы разберём в уроке 8.5. Сейчас важно знать: class — это просто удобный синтаксис, который делает код понятнее. Используйте его — это современный стандарт.
✅ Итоги урока
- class — шаблон для создания множества однотипных объектов. Синтаксический сахар над прототипами
- constructor — специальный метод инициализации, вызывается автоматически при
new - new ClassName(args) — создание экземпляра. Каждый экземпляр независим, имеет свои данные
- Имена классов — в PascalCase:
User,BankAccount,ShoppingCart - static — метод принадлежит классу, не экземпляру. Вызывается как
ClassName.method() - #field — настоящее приватное поле (ES2022). Недоступно снаружи класса на уровне синтаксиса
class— это синтаксический сахар. Под капотом JavaScript использует прототипы
В следующем уроке — наследование: как один класс берёт всё лучшее от другого через extends и super. 🚀
Классы в JavaScript
7 вопросов