Пошук уроків, статей та іншого контенту
Зрозумієте об’єкти, класи, екземпляри та роль ООП у структуруванні JavaScript-програм.
Об’єкт — це структура даних, яка об’єднує властивості та методи.
Властивість описує дані об’єкта.
Метод — це функція, яка належить об’єкту та працює з його даними.
Наприклад, інформацію про користувача можна зберегти в об’єкті:
const user = {
name: "Олена",
age: 25,
isActive: true,
sayHello() {
console.log(`Привіт! Мене звати ${this.name}.`);
}
};
console.log(user.name);
console.log(user.age);
user.sayHello();У цьому прикладі:
name, age та isActive — властивості;
sayHello — метод;
this посилається на поточний об’єкт user;
this.name означає «властивість name поточного об’єкта».
Властивість можна отримати двома способами.
const product = {
title: "Ноутбук",
price: 35000
};
console.log(product.title);
console.log(product.price);Точкова нотація зручна, коли назва властивості відома заздалегідь.
const product = {
title: "Ноутбук",
price: 35000
};
const propertyName = "price";
console.log(product[propertyName]);Квадратні дужки потрібні, коли назва властивості зберігається у змінній або містить символи, які не можна використати в точковій нотації.
const settings = {
"font-size": 16
};
console.log(settings["font-size"]);Об’єкт, оголошений через const, не можна перепризначити, але його властивості можна змінювати:
const user = {
name: "Андрій",
age: 30
};
user.age = 31;
user.city = "Львів";
console.log(user);Змінна user продовжує посилатися на той самий об’єкт, але його вміст змінився.
Властивість можна видалити за допомогою delete:
const user = {
name: "Андрій",
age: 30
};
delete user.age;
console.log(user);Прості типи, наприклад числа, рядки та логічні значення, копіюються як значення. Об’єкти поводяться інакше: змінна зберігає посилання на об’єкт.
const firstUser = {
name: "Марія"
};
const secondUser = firstUser;
secondUser.name = "Олег";
console.log(firstUser.name); // ОлегfirstUser і secondUser посилаються на один і той самий об’єкт. Тому зміна через одну змінну видима через іншу.
Щоб створити поверхневу копію об’єкта, можна використати оператор розгортання:
const firstUser = {
name: "Марія",
age: 28
};
const secondUser = {
...firstUser
};
secondUser.name = "Олег";
console.log(firstUser.name); // Марія
console.log(secondUser.name); // ОлегЦе поверхнева копія. Якщо об’єкт містить вкладені об’єкти або масиви, вони можуть залишатися спільними посиланнями.
Об’єктно-орієнтоване програмування, або ООП, — це підхід до організації коду навколо об’єктів.
Об’єкти можуть об’єднувати:
стан — дані, які описують об’єкт;
поведінку — дії, які цей об’єкт може виконувати.
Наприклад, банківський рахунок має:
стан: власник і баланс;
поведінку: поповнення та зняття коштів.
Без об’єктів логіка могла б виглядати розрізнено:
let balance = 1000;
function deposit(amount) {
balance += amount;
}
function withdraw(amount) {
if (amount <= balance) {
balance -= amount;
}
}Об’єкт дозволяє об’єднати дані та функції, які працюють із цими даними:
const account = {
balance: 1000,
deposit(amount) {
this.balance += amount;
},
withdraw(amount) {
if (amount <= this.balance) {
this.balance -= amount;
}
}
};
account.deposit(500);
account.withdraw(200);
console.log(account.balance); // 1300Такий підхід допомагає структурувати програму та зменшити кількість не пов’язаних між собою змінних.
Клас — це шаблон для створення однотипних об’єктів.
Клас описує:
які властивості матимуть об’єкти;
які методи вони матимуть;
як створювати їх початковий стан.
Клас сам по собі не є конкретним користувачем, товаром чи рахунком. Він лише описує, якими будуть такі об’єкти.
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Привіт! Мене звати ${this.name}.`);
}
}У цьому коді:
class User створює клас User;
constructor — спеціальний метод для початкового налаштування об’єкта;
name та age — властивості майбутніх об’єктів;
sayHello — метод класу.
Назви класів за домовленістю починають із великої літери: User, Product, BankAccount.
Екземпляр — це конкретний об’єкт, створений на основі класу.
Для створення екземпляра використовується оператор new:
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Привіт! Мене звати ${this.name}.`);
}
}
const firstUser = new User("Олена", 25);
const secondUser = new User("Дмитро", 32);
firstUser.sayHello();
secondUser.sayHello();
console.log(firstUser.age); // 25
console.log(secondUser.age); // 32firstUser і secondUser — два різні екземпляри одного класу User.
Вони мають однакову структуру та метод sayHello, але різні дані.
console.log(firstUser === secondUser); // falseКожен виклик new User(...) створює новий об’єкт.
thisМетод constructor викликається автоматично під час створення екземпляра.
class Product {
constructor(title, price) {
this.title = title;
this.price = price;
}
}
const product = new Product("Клавіатура", 2500);
console.log(product.title); // Клавіатура
console.log(product.price); // 2500Параметри title і price отримують значення під час виклику new Product(...).
this.title і this.price записують ці значення у властивості нового екземпляра.
Зазвичай назви параметрів конструктора та властивостей збігаються:
class Book {
constructor(title, author) {
this.title = title;
this.author = author;
}
}Ліворуч від = знаходиться властивість об’єкта, праворуч — параметр конструктора.
Методи описують поведінку екземплярів:
class Counter {
constructor() {
this.value = 0;
}
increment() {
this.value += 1;
}
decrement() {
this.value -= 1;
}
getValue() {
return this.value;
}
}
const counter = new Counter();
counter.increment();
counter.increment();
counter.decrement();
console.log(counter.getValue()); // 1Методи класу не потрібно оголошувати через ключове слово function.
Правильно:
class User {
sayHello() {
console.log("Привіт!");
}
}Неправильно:
class User {
// Так методи класу не оголошують
function sayHello() {
console.log("Привіт!");
}
}Розглянемо повніший приклад:
class BankAccount {
constructor(owner, initialBalance = 0) {
this.owner = owner;
this.balance = initialBalance;
}
deposit(amount) {
if (amount <= 0) {
console.log("Сума поповнення має бути більшою за нуль.");
return;
}
this.balance += amount;
}
withdraw(amount) {
if (amount <= 0) {
console.log("Сума зняття має бути більшою за нуль.");
return;
}
if (amount > this.balance) {
console.log("Недостатньо коштів.");
return;
}
this.balance -= amount;
}
showBalance() {
console.log(`${this.owner}: ${this.balance} грн`);
}
}
const account = new BankAccount("Олена", 1000);
account.showBalance();
account.deposit(500);
account.withdraw(300);
account.showBalance();
account.withdraw(2000);Клас контролює операції над балансом:
рахунок створюється з власником і початковим балансом;
поповнення не приймає нульові та від’ємні суми;
зняття не дозволяє витратити більше коштів, ніж є на рахунку.
Створення класу особливо корисне, коли в програмі має бути багато однотипних об’єктів:
const firstAccount = new BankAccount("Олена", 1000);
const secondAccount = new BankAccount("Дмитро", 2500);
const thirdAccount = new BankAccount("Ірина", 500);
firstAccount.showBalance();
secondAccount.showBalance();
thirdAccount.showBalance();Властивості, які створюються в constructor, належать конкретному екземпляру:
class User {
constructor(name) {
this.name = name;
}
}
const firstUser = new User("Олена");
const secondUser = new User("Дмитро");
firstUser.name = "Марія";
console.log(firstUser.name); // Марія
console.log(secondUser.name); // ДмитроЗміна firstUser.name не змінює secondUser.name.
Методи, оголошені в класі, доступні його екземплярам:
class User {
constructor(name) {
this.name = name;
}
describe() {
return `Користувач: ${this.name}`;
}
}
const user = new User("Олена");
console.log(user.describe());Оператор instanceof перевіряє, чи створений об’єкт на основі певного класу:
class Product {
constructor(title) {
this.title = title;
}
}
const product = new Product("Монітор");
console.log(product instanceof Product); // true
console.log(product instanceof Object); // trueДля звичайного об’єкта, який не створювався через Product, результат буде false:
const anotherProduct = {
title: "Монітор"
};
console.log(anotherProduct instanceof Product); // falseУ JavaScript об’єкти часто створюють без класів:
const user = {
name: "Олена",
sayHello() {
console.log(`Привіт, ${this.name}!`);
}
};Об’єктний літерал добре підходить, коли потрібен один конкретний об’єкт.
Клас варто використовувати, коли:
потрібно створити багато об’єктів однакової структури;
у цих об’єктів є спільні методи;
потрібно зібрати пов’язану логіку в одному місці;
об’єкти мають однакові правила створення та роботи.
Наприклад, для одного налаштування достатньо об’єктного літерала:
const settings = {
theme: "dark",
language: "uk"
};А для багатьох товарів з однаковою поведінкою зручніше використати клас:
class Product {
constructor(title, price) {
this.title = title;
this.price = price;
}
getDescription() {
return `${this.title}: ${this.price} грн`;
}
}
const products = [
new Product("Мишка", 800),
new Product("Клавіатура", 2500),
new Product("Навушники", 3200)
];
for (const product of products) {
console.log(product.getDescription());
}Наслідування дає змогу створити новий клас на основі іншого.
Для цього використовують extends:
class User {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Привіт! Я ${this.name}.`);
}
}
class Admin extends User {
deleteUser() {
console.log("Користувача видалено.");
}
}
const admin = new Admin("Олена");
admin.sayHello();
admin.deleteUser();Admin успадковує від User:
конструктор, якщо не створено власний;
метод sayHello;
можливість мати власні методи, наприклад deleteUser.
Якщо дочірньому класу потрібен власний конструктор, він має викликати super до використання this:
class User {
constructor(name) {
this.name = name;
}
}
class Admin extends User {
constructor(name, permissions) {
super(name);
this.permissions = permissions;
}
}
const admin = new Admin("Олена", ["delete", "edit"]);
console.log(admin.name);
console.log(admin.permissions);У більшості простих програм не потрібно створювати складну ієрархію класів. Наслідування варто застосовувати тоді, коли між типами об’єктів справді є чіткий спільний зв’язок.
За замовчуванням властивості класу є публічними. До них можна звертатися ззовні:
class User {
constructor(name) {
this.name = name;
}
}
const user = new User("Олена");
console.log(user.name);
user.name = "Марія";У сучасному JavaScript приватні поля позначають символом #:
class BankAccount {
#balance;
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500Поле #balance доступне лише всередині класу. Спроба звернутися до нього напряму спричинить помилку:
// account.#balance; // Помилка: приватне поле недоступне ззовніПриватні поля корисні, коли потрібно захистити внутрішній стан об’єкта та дозволити змінювати його лише через контрольовані методи.
newДля створення екземпляра класу потрібно використовувати new:
class User {
constructor(name) {
this.name = name;
}
}
const user = new User("Олена");Виклик User("Олена") без new є помилкою для класу.
thisthis.name — це властивість поточного об’єкта, а просто name може бути локальною змінною або взагалі не існувати.
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}Якщо змінні оголошені поза об’єктом, різні екземпляри можуть випадково використовувати один спільний стан:
let balance = 0;
class Account {
deposit(amount) {
balance += amount;
}
}Зазвичай стан конкретного екземпляра потрібно зберігати через this:
class Account {
constructor() {
this.balance = 0;
}
deposit(amount) {
this.balance += amount;
}
}Присвоєння об’єкта іншій змінній копіює посилання, а не сам об’єкт:
const first = { value: 10 };
const second = first;
second.value = 20;
console.log(first.value); // 20Якщо потрібна окрема копія, використовуйте відповідний спосіб копіювання, наприклад оператор ... для простих поверхневих об’єктів.
Не кожну функцію потрібно перетворювати на клас. Для невеликої кількості пов’язаних даних часто достатньо об’єктного літерала або звичайної функції.
Клас виправданий, коли є:
повторювана структура об’єктів;
спільна поведінка;
правила створення або зміни стану.
Створіть клас TodoItem, який:
отримує в конструкторі текст завдання;
зберігає властивість text;
створює властивість isCompleted зі значенням false;
має метод complete(), який змінює isCompleted на true;
має метод showStatus(), який виводить текст завдання та його статус.
Приклад очікуваного використання:
const todo = new TodoItem("Вивчити класи");
todo.showStatus();
todo.complete();
todo.showStatus();Один із можливих результатів:
Вивчити класи — не виконано
Вивчити класи — виконаноОб’єкт об’єднує властивості та методи.
Властивості зберігають дані, а методи описують поведінку.
this посилається на поточний об’єкт або екземпляр.
Клас — це шаблон для створення однотипних об’єктів.
Екземпляр — конкретний об’єкт, створений за допомогою new.
constructor налаштовує початковий стан екземпляра.
Методи класу описують спільну поведінку його екземплярів.
extends дає змогу створювати дочірні класи.
Не всі об’єкти потрібно створювати через класи: для простих випадків достатньо об’єктних літералів.
ООП допомагає об’єднати дані та пов’язані з ними дії, зробивши програму зрозумілішою та структурованішою.