13. Класи

Тут ми вводимо класи ECMAScript 6. Спочатку ми покажемо, що функції вже можна використовувати як класи.
13.1. скрипт [class-00]
Наступний скрипт демонструє незвичайне використання функцій. Тут їх використовують як об’єкти.
'use strict';
// функцію можна використовувати як об’єкт
// порожня оболонка
function f() {
}
//, якому ззовні присвоюються властивості
f.prop1 = "val1";
f.show = function () {
console.log(this.prop1);
}
// використання f
f.show();
// функція g, що працює як клас
function g() {
this.prop2 = "val2";
this.show = function () {
console.log(this.prop2);
}
}
// інстанціювання функції за допомогою [new]
new g().show();
Коментарі
- рядки 5–7: тіло функції f не визначає жодних властивостей;
- рядки 9–12: властивості функції f задаються ззовні;
- рядок 14: використання функції (об’єкта) f. Зверніть увагу, що ми пишемо не [f()], а просто [f]. Це нотація об’єкта;
- рядки 17–22: визначається функція [g] так, ніби це клас із властивостями та методами;
- рядок 24: функція [g] інстанціюється за допомогою [new g()];
Результати виконання
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-00.js"
val1
val2
ES6 запровадила поняття класу, що відтепер дозволяє нам уникати використання функцій для створення класів.
13.2. скрипт [class-01]
Скрипт [class-01] представляє клас [Personne]:
// клас
class Personne {
// конструктор
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// методи getter та setter
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString у JSON
toString() {
return JSON.stringify(this);
}
}
// виклик класу
function main() {
const personne = new Personne("Poirot", "Hercule", 66);
console.log("personne=", personne.toString(), typeof (personne), personne instanceof (Personne));
}
// виклик методу main
main();
Коментарі
- рядок 2: ключове слово [class] позначає клас;
- рядки 5–9: ключове слово [constructor] позначає конструктор класу. Він може бути лише один. Він слугує для створення та ініціалізації екземпляра класу. Зверніть увагу, що оголошення властивостей [nom, prénom, âge] відсутнє;
- рядки 11–36: властивості класу. Тут містяться елементи, які ми вже розглядали в розділі про об’єкти 4 на сторінці 44. Відрізняється лише синтаксис;
- рядок 41: створення об’єкта типу [Personne]. Відтепер об’єкт [personne] використовується як літеральний об’єкт. [typeof (personne)] дорівнює «object», а вираз [personne instanceof (Personne)] є істинним. Отже, можна визначити точний тип екземпляра класу;
Результати виконання
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-01.js"
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66} object true
13.3. скрипт [class-02]
Цей скрипт демонструє можливість успадкування класу за допомогою ключового слова [extends].
Спочатку виділяємо клас [Personne] у модуль [Personne.js]:
// клас
class Personne {
// конструктор
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// методи getter та setter
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString у JSON
toString() {
return JSON.stringify(this);
}
}
// експорт класу
export default Personne;
- рядок 39: ми експортуємо клас [Personne], щоб інші скрипти могли його імпортувати;
Скрипт [class-02] створює клас [Enseignant], похідний від класу [Personne]:
// імпорт
import Personne from './Personne';
// клас
class Enseignant extends Personne {
// конструктор
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// методи getter та setter
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// виклик класу
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// виклик main
main();
Коментарі
- рядок 2: імпортується клас [Personne] з модуля [Personne.js], який знаходиться в тій самій папці, що й [class-02];
- рядок 5: клас [Enseignant] розширює (успадковує) клас [Personne] за допомогою ключового слова [extends]: він додає до нього властивість [_discipline] із відповідними методами getter та setter;
- рядки 8–11: конструктор класу [Enseignant] отримує чотири значення для ініціалізації чотирьох властивостей класу;
- рядок 9: ключове слово [super] викликає конструктор батьківського класу [Personne], який, таким чином, ініціалізує властивості [_nom, _prénom, _âge];
- рядок 10: ініціалізується властивість [_discipline], що належить класу [Enseignant];
- рядки 14–19: геттер і сеттер властивості [_discipline];
- рядок 25: створюється об’єкт типу [Enseignant];
- рядок 26: використовується метод [enseignant.toString()]. Клас [Enseignant] не має цього методу. Тоді автоматично використовується метод його батьківського класу. Цей метод робить вираз [JSON.stringify(this)], де [this] тут буде об’єктом [Enseignant], а не об’єктом [Personne]. У об’єктно-орієнтованому програмуванні це називається поліморфізмом класів. Це складне поняття для JavaScript, який не є об’єктно-орієнтованою мовою. Проте JavaScript у цьому випадку робить те, чого від нього очікують: він правильно відображає вчителя;
Результати виконання такі:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-02.js"
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
- рядок 2: JavaScript правильно розпізнає, що змінна [enseignant] має тип [Enseignant];
13.4. скрипт [class-03]
Скрипт [class-03] демонструє, що дочірній клас може перевизначати властивості та методи свого батьківського класу. Тут ми перевизначаємо метод [toString] батьківського класу:
// імпорти
import Personne from './Personne';
// клас
class Enseignant extends Personne {
// конструктор
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// методи getter та setter
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
// перевизначення toString
toString() {
return "[Enseignant]" + JSON.stringify(this);
}
}
// виклик класу
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// виклик функції main
main();
Результати виконання такі:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-03.js"
enseignant= [Enseignant]{"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
13.5. скрипт [class-04]
Скрипт [class-04] знову демонструє поліморфізм у дії: там, де функція очікує формальний параметр типу [Personne], можна передати похідний тип, такий як [Enseignant]. Адже завдяки похідності тип [Enseignant] має всі атрибути типу [Personne].
Спочатку ми виділяємо тип [Enseignant] у модуль [Enseignant.js]:
// імпорт
import Personne from './Personne';
// клас
class Enseignant extends Personne {
// конструктор
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// методи getter та setter
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// експорт класу
export default Enseignant;
- рядок 24: клас [Enseignant] експортується, щоб інші скрипти могли його імпортувати;
Скрипт [class-04] має такий вигляд:
// імпорт
import Enseignant from './Enseignant';
import Personne from './Personne';
// функція, що приймає людину як параметр
function show(personne) {
// у всіх випадках
console.log("paramètre=", personne.toString(), typeof (personne));
// екземпляр класу «Людина»
if (personne instanceof Personne) {
console.log("personne=", personne.toString());
}
// екземпляр класу «Викладач»
if (personne instanceof Enseignant) {
console.log("enseignant=", personne.toString());
}
}
// виклик функції show з параметром «викладач»
show(new Enseignant("Poirot", "Hercule", 66, "détective"));
show(new Personne("Marple", "Miss", 70));
- рядок 6: функція [show] очікує тип [Personne] або його похідний;
- рядок 8: виводиться рядок параметра та його тип. Ми побачимо [object];
- рядки 10–16: можна визначити, чи це тип [Personne], чи тип [Enseignant]. Отже, код можна адаптувати до фактичного типу параметра;
Результати виконання такі:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-04.js"
paramètre= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
paramètre= {"_nom":"Marple","_prénom":"Miss","_âge":70} object
personne= {"_nom":"Marple","_prénom":"Miss","_âge":70}
- рядки 4 та 6: JavaScript правильно розпізнає тип екземплярів класу;