JS об'єкт Class
Визначення класів
Класи в дійсності є "спеціальними функціями". Так само, як ви може визначити вирази та декларації функцій, можна визначити класи двома шляхами: використовуючи вираз класу або його декларацію.
// Декларація
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// Вираз; клас є анонімним, але призначений змінній
const Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// Вираз; клас має своє власне ім'я
const Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Такі вирази класів можуть бути як анонімними, так і мати власне ім'я, відмінне від імені змінної, до якої вони присвоєні. Проте, на відміну від декларацій функцій, декларації класів мають ті ж самі обмеження "мертвої зони", як і ключові слова let
та const
, і ведуть себе так, ніби не відбувається їхнє переміщення на початок області видимості
Тіло класу
Тіло класу розташоване у фігурних дужках {}
. Саме тут ви визначаєте члени класу, такі як методи чи конструктор.
Тіло класу виконується в строгому режимі навіть без директиви "use strict".
Елемент класу може бути охарактеризований трьома аспектами:
Тип: Геттер (getter
), сеттер (setter
), метод або поле.
Розташування: Статичне чи екземпляра.
Видимість: Публічний чи приватний.
Разом вони утворюють 16 можливих комбінацій.
Конструктор
Конструктор - це спеціальний метод для створення та ініціалізації об'єкта, створеного класом. В класі може бути лише один спеціальний метод із ім'ям "конструктор" — якщо клас містить більше одного входження методу конструктора, буде викинута помилка SyntaxError
.
Конструктор може використовувати ключове слово super
для виклику конструктора батьківського класу.
Ви можете створювати властивості екземпляра всередині конструктора:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Блоки статичної ініціалізації
Блоки статичної ініціалізації відкривають широкі можливості для налаштування статичних властивостей. Вони дозволяють проводити оцінювання виразів на етапі ініціалізації і надають доступ до приватної області видимості.
Можливе оголошення декількох статичних блоків, які можна комбінувати з декларуванням статичних полів і методів. При цьому всі статичні елементи будуть оцінюватися в послідовності їх оголошення.
Методи
Кожен метод визначається в прототипі відповідного екземпляра класу і є спільним для всіх його екземплярів. Ці методи можуть бути представлені у вигляді звичайних функцій, асинхронних функцій, функцій-генераторів або асинхронних функцій-генераторів.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
*getSides() {
yield this.height;
yield this.width;
yield this.height;
yield this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
console.log([...square.getSides()]); // [10, 10, 10, 10]
Статичні методи та поля у класах
Ключове слово static
слугує для визначення статичних методів або полів класу. На відміну від звичайних властивостей, які відносяться до конкретного екземпляра, статичні властивості встановлюються на рівні самого класу. Статичні методи зазвичай використовуються як універсальні функції для додатків, а статичні поля можна застосовувати для кешування, фіксованих конфігурацій або для даних, які не вимагають копіювання між різними екземплярами.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined
console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755
Оголошення полів в класах
Використовуючи синтаксис оголошення полів класу, наступний конструктор можна виразити так:
class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Поля класу схожі на властивості об'єктів, а не змінні, тому для їх оголошення ми не вживаємо ключові слова типу const
. У JavaScript
для приватних елементів використовується специфічний синтаксис, тому слова, такі як public
чи private
, також не вживаються.
Як показано вище, поля можуть бути оголошені зі значенням за замовчуванням або без нього. Поля без цього значення автоматично приймають undefined
. Завдяки передбачуваному оголошенню полів, визначення класів стає більш прозорим, а постійна наявність полів сприяє оптимізації коду.
Приватні елементи класу
Використовуючи приватні поля, можна докладніше визначити клас наступним чином:
class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}
Спроба звернутися до приватних полів поза межами класу призведе до помилки; ці поля доступні лише всередині класу. Встановлюючи елементи недоступними зовні, ви запобігаєте небажаній залежності від внутрішнього устрою класу, який може змінюватись.
Оголошення приватних полів можливе лише на стадії їх визначення. Неможливо створити їх пізніше через присвоєння, на відміну від звичних властивостей класу.
Спадкування в класах
Для створення класу як дочірнього елементу іншого конструктора (класу або функції) використовується ключове слово extends
.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // здійснити виклик конструктора батьківського класу та передати йому ім'я в якості параметру
}
speak() {
console.log(`${this.name} barks.`);
}
}
const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.
Якщо в підкласі існує конструктор, перед використанням this
необхідно спочатку звернутися до super()
. Ключове слово super
може бути також використане для виклику методів батьківського класу, що відповідають поточному контексту.
class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(`${this.name} roars.`);
}
}
const l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.
Порядок оцінки
- Спочатку оцінюється умовний блок
extends
, якщо він існує. Він має представляти собою відповідну конструкторську функцію абоnull
; в іншому випадку буде викликаноTypeError
. - Витягується метод конструктора та замінюється стандартною реалізацією, якщо конструктор відсутній. Однак цей крок невидимий, оскільки визначення конструктора є лише методом.
- Ключі властивостей елементів класу розглядаються послідовно. Якщо ключ властивості обчислюється, виконується вираз зі значенням
this
, яке відповідає оточуючому класу, а не самому класу. Значення властивості залишаються незмінними. - Методи та аксесори впорядковуються відповідно до оголошення. Методи екземпляру й аксесори додаються до властивості
prototype
класу, а статичні — безпосередньо до класу. Приватні методи та аксесори зберігаються для пізнішого використання. - Клас ініціалізується з використанням
prototype
таextends
, якщо вони вказані. При спробі доступу до імені класу в раніше зазначених кроках виникаєReferenceError
, оскільки клас ще не готовий. - Значення елементів класу визначаються в порядку їхнього введення:
— Для кожного екземпляра зберігається вираз ініціалізатора.
— Цей вираз виконується під час створення екземпляра, в початковому конструкторі або перед завершенням виклику
super()
. — Для статичних полів відбувається аналогічний процес, зthis
вказівником на сам клас. — Статичні блоки ініціалізації обробляються так само. - Нарешті, клас повністю ініціалізований і готовий до використання як конструктор.
Нотатка: | Приватні особливості мають обмеження, що всі назви властивостей, визначених у одному класі, повинні бути унікальними. Усі інші публічні властивості не мають цього обмеження — у вас може бути декілька публічних властивостей з однаковими назвами, і остання перезапише попередні. |
Синтаксис
class ClassName {
constructor() { ... }
method_1() { ... }
method_2() { ... }
method_3() { ... }
}
Переглядачі
Переглядач | |||||
---|---|---|---|---|---|
49 |
45 |
9 |
36 |
13 |
Переглядач | ||||
---|---|---|---|---|
49 |
49 |
45 |
9 |
Переглядач | ||
---|---|---|
6.0.0 |
1.0 |
Приклади
Ми можемо використовувати класи для створення ієрархій наслідування. Розглянемо клас Animal
, з якого наслідується клас Dog
.
Клас Dog
наслідує властивості та методи класу Animal
, але перевизначає метод sound()
, щоб відобразити специфічну для собак поведінку.
class Animal {
constructor(name) {
this.name = name;
}
sound() {
console.log(this.name + ' makes a sound');
}
}
class Dog extends Animal {
sound() {
console.log(this.name + ' barks');
}
}
const rex = new Dog('Rex');
rex.sound(); // Виведе "Rex barks"
JavaScript дозволяє нам використовувати приватні властивості в класах за допомогою #
.
У цьому прикладі #balance
є приватною властивістю, і її можна змінити тільки за допомогою методів в межах класу BankAccount
. Це допомагає контролювати доступ до важливих даних.
class BankAccount {
#balance = 0;
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
this.#balance += amount;
}
withdraw(amount) {
if (amount <= this.#balance) {
this.#balance -= amount;
} else {
console.log('Insufficient funds in the account');
}
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(1000);
account.withdraw(200);
console.log(account.getBalance()); // Виведе "800"
Методи
constructor()
- Ініціалізує екземпляр класу при його створенні.