--- title: Класи slug: Web/JavaScript/Reference/Classes tags: - ECMAScript 2015 - JavaScript - Класи - конструктори - наслідування translation_of: Web/JavaScript/Reference/Classes ---
{{JsSidebar("Classes")}}

Класи в JavaScript були додані в стандарті ECMAScript 6 і є насамперед синтаксичним цукром для існуючого прототипного наслідування у JavaScript. Синтаксис класів не впроваджує нову модель об'єктно-орієнтованого наслідування у JavaScript.

Визначення класів

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

Оголошення класу

Одним зі способів визначення класу є оголошення класу. Для оголошення класу використовується ключове слово class з назвою класу (у прикладі нижче "Rectangle").

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

Підняття

Важлива відмінність між оголошеннями функції та класу полягає в тому, що оголошення функції {{Glossary("Hoisting", "піднімається")}}, а оголошення класу ні. Спочатку необхідно оголосити клас, і тільки потім звертатись до нього, інакше подібний код викине помилку {{jsxref("ReferenceError")}}:

const p = new Rectangle(); // ReferenceError

class Rectangle {}

Вираз класу

Вираз класу - це інший спосіб визначення класу. Вираз класу може бути іменованим або неіменованим. Ім'я, надане іменованому виразу класу, є локальним для тіла класу. (Хоча його можна отримати через властивість класу (не екземпляра) {{jsxref("Function.name", "name")}}).

// не іменований
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// виведе "Rectangle"

// іменований
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// виведе "Rectangle2"

Заувага: Вирази класів підпадають під такі самі обмеження підняття, які описані у розділі {{anch("Оголошення класу")}}.

Тіло класу та визначення методів

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

Строгий режим

Код у тілі класу виконується у строгому режимі, тобто, код, записаний тут, є предметом більш строгого синтаксису заради підвищення продуктивності. Деякі помилки, які пройшли б непоміченими, будуть викидатися, а певні ключові слова є зарезервованими для майбутніх версій ECMAScript.

Конструктор

Метод constructor - це спеціальний метод для створення та ініціалізації об'єктів, створених ключовим словом class. У класі може бути лише один спеціальний метод з ім'ям "constructor". Якщо клас містить більше одного метода constructor, виникне помилка {{jsxref("SyntaxError")}}.

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

Методи прототипу

Дивіться також визначення методів

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // гетер
  get area() {
    return this.calcArea();
  }
  // метод
  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100

Статичні методи

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

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    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.distance; //undefined
p2.distance; //undefined

console.log(Point.distance(p1, p2)); // 7.0710678118654755

Пакування через прототипні та статичні методи

Коли статичний або прототипний метод викликається без значення для this, значенням this всередині метода буде undefined. Ця поведінка буде такою самою навіть якщо директива "use strict" не задана, тому що код в межах тіла класу завжди виконується у строгому режимі.

class Animal {
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined

Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined

Якщо наведений приклад написаний з використанням традиційного синтаксису функцій, то автоматичне пакування при викликах методів відбуватиметься у нестрогому режимі, на основі початкового значення this. Якщо початковим значенням є undefinedthis буде присвоєний глобальний об'єкт.

Автопакування не відбудеться у строгому режимі, значення this залишиться незмінним.

function Animal() { }

Animal.prototype.speak = function() {
  return this;
}

Animal.eat = function() {
  return this;
}

let obj = new Animal();
let speak = obj.speak;
speak(); // global object

let eat = Animal.eat;
eat(); // global object

Властивості екземпляра

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

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

Статичні (на боці класу) властивості-значення та прототипні властивості-значення повинні визначатися за межами оголошення класу:

Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;

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

Оголошення публічних та приватних полів є експериментальною функціональністю (stage 3), запропонованою на TC39, комітеті стандартів JavaScript. Підтримка у переглядачах є обмеженою, але функціональність можна використовувати через збирачі пакетів з системами на кшталт Babel.

Оголошення публічних полів

З синтаксисом оголошення полів JavaScript наведений вище приклад можна записати наступним чином:

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

З оголошенням полів заздалегідь оголошення класу стає більш самозадокументованим, а поля завжди присутні.

Як бачимо, оголошені поля можуть мати або не мати значення за замовчуванням.

Оголошення приватних полів

З використанням приватних полів визначення можна вдосконалити наступним чином.

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

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

Приватні поля можуть оголошуватися лише одразу при оголошенні полів.

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

Щоб дізнатись більше, дивіться також поля класу.

Створення підкласу за допомогою extends

Ключове слово extends використовують у оголошенні класу або у виразі класу, щоб створити клас, як дочірній від іншого класу.

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} видає звуки.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // викликати конструктор суперкласу та передати параметром ім'я
  }

  speak() {
    console.log(`${this.name} гавкає.`);
  }
}

let d = new Dog('Мінні');
d.speak(); // Мінні гавкає.

Якщо у підкласі присутній конструктор, він має спочатку викликати super(), перед використанням "this".

Можна також розширювати через extend традиційні "класи" на основі функцій:

function Animal (name) {
  this.name = name;
}

Animal.prototype.speak = function () {
  console.log(`${this.name} видає звуки.`);
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} гавкає.`);
  }
}

let d = new Dog('Мінні');
d.speak(); // Мінні гавкає.

//Заувага: Для однойменних методів, дочірній метод переважає над батьківським

Зауважте, що класи не можуть розширювати звичайні об'єкти (не конструктори). Якщо бажаєте успадкуватись від звичайного об'єкта, можете натомість використати {{jsxref("Object.setPrototypeOf()")}}:

const Animal = {
  speak() {
    console.log(`${this.name} видає звуки.`);
  }
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}

// Якщо ви цього не зробите, то отримаєте помилку, викликавши speak
Object.setPrototypeOf(Dog.prototype, Animal);

let d = new Dog('Мінні');
d.speak(); // Мінні видає звуки.

Species

Можливо, ви захочете повернути об'єкти {{jsxref("Array")}} у своєму похідному класі масиву MyArray. Шаблон species дозволяє перевизначати початкові конструктори.

Наприклад, використовуючи такі методи, як {{jsxref("Array.map", "map()")}}, що повертає конструктор за замовчуванням, ви хочете, щоб ці методи повертали батьківський об'єкт Array замість об'єкту MyArray. Символ {{jsxref("Symbol.species")}} дозволяє це зробити:

class MyArray extends Array {
  // Заміщення Symbol.species батьківським конструктором Array
  static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true

Виклик методів суперкласу за допомогою super

Ключове слово super використовують для виклику відповідних методів суперкласу. Це одна з переваг над прототипним наслідуванням.

class Cat {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name + ' видає звуки.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' ричить.');
  }
}

Домішки

Абстрактні підкласи, або домішки, це шаблони для створення класів. В ECMAScript підклас може мати лише один суперклас, тому множинне наслідування, наприклад, кількох класів інструментів, неможливе. Функціональність має бути надана суперкласом. 

У ECMAScript для реалізації домішок може використовуватись функція з суперкласом в якості параметра та підкласом, що розширює цей суперклас, в якості значення, що повертається:

let calculatorMixin = Base => class extends Base {
  calc() { }
};

let randomizerMixin = Base => class extends Base {
  randomize() { }
};

Клас, що використовує ці домішки, може бути записаний так:

class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

Специфікації

Специфікація Статус Коментар
{{SpecName('ES2015', '#sec-class-definitions', 'Class definitions')}} {{Spec2('ES2015')}} Початкове визначення.
{{SpecName('ES2016', '#sec-class-definitions', 'Class definitions')}} {{Spec2('ES2016')}}
{{SpecName('ES2017', '#sec-class-definitions', 'Class definitions')}} {{Spec2('ES2017')}}
{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}} {{Spec2('ESDraft')}}

Сумісність з веб-переглядачами

{{Compat("javascript.classes")}}

Запуск у Чернетці

Клас не можна перевизначати. Якщо ви працюєте з кодом у Чернетці (меню Firefox Інструменти > Веб-розробка > Чернетка), і 'Запускаєте' визначення класу з тим самим ім'ям двічі, ви отримаєте дивну помилку SyntaxError: redeclaration of let <ім'я-класу>.

Щоб перезапустити визначення, використовуйте меню Чернетки Виконати > Перезавантажити і запустити.
Будь ласка, голосуйте за помилку #1428672.

Див. також