From 218934fa2ed1c702a6d3923d2aa2cc6b43c48684 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:43:23 -0500 Subject: initial commit --- .../uk/web/javascript/reference/classes/index.html | 418 +++++++++++++++++++++ 1 file changed, 418 insertions(+) create mode 100644 files/uk/web/javascript/reference/classes/index.html (limited to 'files/uk/web/javascript/reference/classes/index.html') diff --git a/files/uk/web/javascript/reference/classes/index.html b/files/uk/web/javascript/reference/classes/index.html new file mode 100644 index 0000000000..ec1694bfd1 --- /dev/null +++ b/files/uk/web/javascript/reference/classes/index.html @@ -0,0 +1,418 @@ +--- +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.

+ +

Див. також

+ + -- cgit v1.2.3-54-g00ecf