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, і ведуть себе так, ніби не відбувається їхнє переміщення на початок області видимості

автор: Bond

Тіло класу

Тіло класу розташоване у фігурних дужках {}. Саме тут ви визначаєте члени класу, такі як методи чи конструктор.

Тіло класу виконується в строгому режимі навіть без директиви "use strict".

Елемент класу може бути охарактеризований трьома аспектами:

Тип: Геттер (getter), сеттер (setter), метод або поле. Розташування: Статичне чи екземпляра. Видимість: Публічний чи приватний.

Разом вони утворюють 16 можливих комбінацій.

автор: Bond

Конструктор

Конструктор - це спеціальний метод для створення та ініціалізації об'єкта, створеного класом. В класі може бути лише один спеціальний метод із ім'ям "конструктор" — якщо клас містить більше одного входження методу конструктора, буде викинута помилка SyntaxError.

Конструктор може використовувати ключове слово super для виклику конструктора батьківського класу.

Ви можете створювати властивості екземпляра всередині конструктора:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
автор: Bond

Блоки статичної ініціалізації

Блоки статичної ініціалізації відкривають широкі можливості для налаштування статичних властивостей. Вони дозволяють проводити оцінювання виразів на етапі ініціалізації і надають доступ до приватної області видимості.

Можливе оголошення декількох статичних блоків, які можна комбінувати з декларуванням статичних полів і методів. При цьому всі статичні елементи будуть оцінюватися в послідовності їх оголошення.

автор: Bond

Методи

Кожен метод визначається в прототипі відповідного екземпляра класу і є спільним для всіх його екземплярів. Ці методи можуть бути представлені у вигляді звичайних функцій, асинхронних функцій, функцій-генераторів або асинхронних функцій-генераторів.

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]
автор: Bond

Статичні методи та поля у класах

Ключове слово 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
автор: Bond

Оголошення полів в класах

Використовуючи синтаксис оголошення полів класу, наступний конструктор можна виразити так:

class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

Поля класу схожі на властивості об'єктів, а не змінні, тому для їх оголошення ми не вживаємо ключові слова типу const. У JavaScript для приватних елементів використовується специфічний синтаксис, тому слова, такі як public чи private, також не вживаються.

Як показано вище, поля можуть бути оголошені зі значенням за замовчуванням або без нього. Поля без цього значення автоматично приймають undefined. Завдяки передбачуваному оголошенню полів, визначення класів стає більш прозорим, а постійна наявність полів сприяє оптимізації коду.

автор: Bond

Приватні елементи класу

Використовуючи приватні поля, можна докладніше визначити клас наступним чином:

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

Спроба звернутися до приватних полів поза межами класу призведе до помилки; ці поля доступні лише всередині класу. Встановлюючи елементи недоступними зовні, ви запобігаєте небажаній залежності від внутрішнього устрою класу, який може змінюватись.

Оголошення приватних полів можливе лише на стадії їх визначення. Неможливо створити їх пізніше через присвоєння, на відміну від звичних властивостей класу.

автор: Bond

Спадкування в класах

Для створення класу як дочірнього елементу іншого конструктора (класу або функції) використовується ключове слово 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.

автор: Bond

Порядок оцінки

  • Спочатку оцінюється умовний блок extends, якщо він існує. Він має представляти собою відповідну конструкторську функцію або null; в іншому випадку буде викликано TypeError.
  • Витягується метод конструктора та замінюється стандартною реалізацією, якщо конструктор відсутній. Однак цей крок невидимий, оскільки визначення конструктора є лише методом.
  • Ключі властивостей елементів класу розглядаються послідовно. Якщо ключ властивості обчислюється, виконується вираз зі значенням this, яке відповідає оточуючому класу, а не самому класу. Значення властивості залишаються незмінними.
  • Методи та аксесори впорядковуються відповідно до оголошення. Методи екземпляру й аксесори додаються до властивості prototype класу, а статичні — безпосередньо до класу. Приватні методи та аксесори зберігаються для пізнішого використання.
  • Клас ініціалізується з використанням prototype та extends, якщо вони вказані. При спробі доступу до імені класу в раніше зазначених кроках виникає ReferenceError, оскільки клас ще не готовий.
  • Значення елементів класу визначаються в порядку їхнього введення: — Для кожного екземпляра зберігається вираз ініціалізатора. — Цей вираз виконується під час створення екземпляра, в початковому конструкторі або перед завершенням виклику super(). — Для статичних полів відбувається аналогічний процес, з this вказівником на сам клас. — Статичні блоки ініціалізації обробляються так само.
  • Нарешті, клас повністю ініціалізований і готовий до використання як конструктор.
автор: Bond

Нотатка

Приватні особливості мають обмеження, що всі назви властивостей, визначених у одному класі, повинні бути унікальними. Усі інші публічні властивості не мають цього обмеження — у вас може бути декілька публічних властивостей з однаковими назвами, і остання перезапише попередні.

автор: Bond

Синтаксис

class ClassName {
  constructor() { ... }
  method_1() { ... }
  method_2() { ... }
  method_3() { ... }
}

методи

Метод Опис
constructor Ініціалізує екземпляр класу при його створенні.

властивості

Властивість Опис
extends Дозволяє класу успадковувати властивості та методи від іншого класу.
static Визначає методи або властивості, які належать безпосередньо до класу, а не до його екземплярів.
super Дозволяє викликати методи батьківського класу в дочірньому класі.

Переглядачі

Підтримка: стаціонарні переглядачі
Переглядач Google Chrome Mozilla Firefox Apple Safari Opera Microsoft EDGE
49 45 9 36 13
Підтримка: мобільні переглядачі
Переглядач Mobile Google Android Mobile Google Chrome Mobile Mozilla Firefox Mobile Apple Safari
49 49 45 9
Підтримка: серверні переглядачі
Переглядач Node.js Deno
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"
автор: Bond

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"
автор: Bond

Коментарі

Коментарів ще немає — будьте першим!

Відповідь для

Увійдіть — коментар з'явиться одразу, без капчі й очікування перевірки.

Пропозиція правки

Ваша версія — : після перевірки замінить чинну. Нова порада чи нотатка — з'явиться на сторінці після перевірки.

Ви гість — діють добові ліміти. Увійдіть, щоб пропонувати без капчі.

Ваша версія секції «»: після перевірки замінить чинну.

Ви гість — діють добові ліміти. Увійдіть, щоб пропонувати без капчі.

Ваша версія опису «»: після перевірки замінить чинну.

Ви гість — діють добові ліміти. Увійдіть, щоб пропонувати без капчі.

Зворотний зв'язок
Вхід