--- title: Класи slug: Web/JavaScript/Reference/Classes tags: - ECMAScript 2015 - JavaScript - Класи - конструктори - наслідування translation_of: Web/JavaScript/Reference/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. Якщо початковим значенням є undefined
, this буде присвоєний глобальний об'єкт.
Автопакування не відбудеться у строгому режимі, значення 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(); // Мінні видає звуки.
Можливо, ви захочете повернути об'єкти {{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')}} |
The compatibility table on this page is generated from structured data. If you'd like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.
{{Compat("javascript.classes")}}
Клас не можна перевизначати. Якщо ви працюєте з кодом у Чернетці (меню Firefox Інструменти > Веб-розробка > Чернетка), і 'Запускаєте' визначення класу з тим самим ім'ям двічі, ви отримаєте дивну помилку SyntaxError: redeclaration of let <ім'я-класу>.
Щоб перезапустити визначення, використовуйте меню Чернетки Виконати > Перезавантажити і запустити.
Будь ласка, голосуйте за помилку #1428672.