Наследование и полиморфизм в JavaScript
1. Наследование в ECMAScript
Перед изучением "Наследование вECMAScript", удостоверьтесь, что у вас уже есть представление о "Классеиобъекте", если нет, давайте изучим:
ECMAScript позволяет вам создать расширенный класс из одного или многих других классов. Этот класс называется производный класс (derived class) или просто подкласс.
Подкласс унаследует property, методы и другие члены от родительского класса. Он так же может переопределить(override) методы из родительского класса. В ECMAScript каждый класс имеет только 1 единственный constructor. Если класс сам не определяет свой собственный constructor, он унаследует constructor родительского класс. Наоборот, если подкласс определяет свой constructor, то не унаследует constructor родительского класса.
В отличие от Java, CSharp и некоторых других языков, ECMAScript допускает множественное наследование. Класс может быть расширен из одного или нескольких родительских классов.
Нам нужно несколько классов для примера.
- Animal (Животное): Класс имитирует животное.
- Duck (Утка): Класс имитирует утку, это подкласс Животного.
- Cat (Кот): Класс имитирует кота, это подкласс Животного.
- Mouse (Мышь): Класс имитирует мышь, это подкласс Животного.
В ECMAScript, конструктор (constructor) используется для создания объекта и прикрепляет значение property (свойства). Конструктор подклассов всегда называется конструктором родителя, чтобы инициализировать значение для property родительского класса, затем он прикрепляет значение этим атрибутам.
Например:
Cat является подклассом, унаследованным от класса Animal, он тоже имеет property этого класса.
inherit-example1.js
class Animal {
constructor(name) {
this.name = name;
}
showInfo() {
console.log("I'm " + this.name);
}
move() {
console.log("Moving..");
}
}
class Cat extends Animal {
constructor(name, age, height) {
super(name);
// Cat's properties:
this.age = age;
this.height = height;
}
// Переопределить (override) метод родительского класса.
showInfo() {
console.log("My name is " + this.name);
}
// Other method...
sleep() {
console.log("Sleeping..");
}
}
// ------------- TEST --------------------
let tom = new Cat("Tom", 3, 20);
console.log("Call move() method");
tom.move();
console.log("\n");
console.log("Call showInfo() method");
tom.showInfo();
Output:
Call move() method
Moving..
Call showInfo() method
My name is Tom
Что случится, когда вы создаёте объект конструктором (constructor)? Как он вызовет коструктор родительского класса? Смотрите иллюстрацию ниже:
С иллюстрации выше вы видите, что конструктор (constructor) родителькского класса вызывается в конструкторе подкласса, она присвоит значения properties родительского класса, затем и properties подкласса.
2. Переопределить метод
По умолчанию, подкласс унаследует методы из родительского класса, но подкласс может переопределять (override) методы родительского класса.
inheritance-example2.js
class Animal {
constructor(name) {
this.name = name;
}
showInfo() {
console.log("I'm " + this.name);
}
move() {
console.log("Moving..");
}
}
class Mouse extends Animal {
constructor(name, age, height) {
super(name);
this.age = age;
this.height = height;
}
// Переопределить (override) метод родительского класса.
showInfo() {
// Вызвать метод showInfo() родительского класса.
super.showInfo();
console.log ("Age: " + this.age);
console.log ("Height: " + this.height);
}
// Переопределить (override) метод родительского класса.
move() {
console.log("Mouse Moving..");
}
}
// ------------- TEST --------------------
let jerry = new Mouse("Jerry", 3, 5);
console.log("Call move() method");
jerry.move();
console.log("\n");
console.log("Call showInfo() method");
jerry.showInfo();
Output:
Call move() method
Mouse Moving..
Call showInfo() method
I'm Jerry
Age: 3
Height: 5
3. Абстрактный метод
Понятие абстракного метода (abstract method) или абстракного класса (abstract class) имеется в таких языках как Java, C#. Но не полностью определяется в ECMAScript. Все же у нас есть способ определить его.
Класс, называется абстрактным (abstract), определяет абстрактные методы и подкласс должен переопределять (override) эти методы, если хочет их использовать. Абстрактные методы всегда вызывают исключение "Not Implemented Error".
abstract-example.js
// An abstract class.
class AbstractDocument {
constructor(name) {
this.name = name
}
// A method can not be used, because it always throws an error.
show() {
// Not Implemented Error
throw "Subclass must implement abstract method";
}
}
class PDF extends AbstractDocument {
// Override method of parent class
show() {
console.log("Show PDF document:", this.name);
}
}
class Word extends AbstractDocument {
show() {
console.log("Show Word document:", this.name)
}
}
// -------------------- TEST -------------------------
let doc1 = new PDF("Python tutorial");
let doc2 = new Word("Java IO Tutorial");
let doc3 = new PDF("ECMAScript Date & Time Tutorial");
let documents = [doc1, doc2, doc3];
for (let i = 0; i < documents.length; i++) {
documents[i].show();
}
let doc4 = new AbstractDocument("An Abstract Document");
doc4.show(); // Throw Error!!!
В приведенном выше примере демонстрируется полиморфизм (Polymorphism) в ECMAScript. Объект Document (документ) может быть представлен в различных формах (PDF, Word, Excel, ...).
Другой пример иллюстрирует полиморфизм: когда я говорю об азиатском человеке, это довольно абстрактно, он может быть японцем, вьетнамцем или индийцем. Однако, у них у всеъ есть особенности азиатских людей.
4. Операторы instanceof, typeof
instanceof
Oператор instanceof помогает вам проверить, является ли "что-то" объектом определенного класса или нет.
instanceof-example.js
class Person {
}
// A Child class of Person
class AsianPerson extends Person {
}
class Triangle {
}
let person = new Person();
let asianPerson = new AsianPerson();
let triangle = new Triangle();
let isPerson = person instanceof Person;
console.log( isPerson ); // true
console.log( asianPerson instanceof Person ); // true
console.log( person instanceof AsianPerson ); // false
console.log( triangle instanceof Person ); // false
console.log( triangle instanceof AsianPerson ); // false
typeof
Оператор typeof используется для проверки вида "чего-то", полученный результат будет название вида.
undefined | "undefined" |
null | "object" |
boolean | "boolean" |
number | "number" |
String | "string" |
Symbol (new in ECMAScript 6) | "symbol" |
Host object (provided by the JS environment) | Implementation-dependent |
Function object (implements [[Call]] in ECMA-262 terms) | "function" |
Any other object | "object" |
typeof-example.js
// A Class:
class Person {
}
// An Object:
let person = new Person();
console.log( typeof Person ); // function
console.log( typeof person ); // object
// null:
console.log( typeof null ); // object
// A Number:
let intValue = 100;
console.log( typeof intValue ); // number
// NaN (Not a Number)
console.log( typeof NaN); // number
// A String
let strValue = "Hello";
console.log( typeof strValue ); // string
// A Boolean:
let boolValue = true;
console.log( typeof boolValue ); // boolean
// undefined
console.log( typeof undefined); // undefined
// An Array
let years = [1980, 2003, 2018];
console.log( typeof years ); // object
// A Function
let myFunc = function() {
}
console.log( typeof myFunc ); // function
// A Symbol
let mySymbol = Symbol();
console.log( typeof mySymbol ); // symbol
Is Sub Class?
Например у вас есть 2 класса A & B. Пример ниже поможет вам проверить является ли A подклассом у B или нет.
let isChild = A === B || A.prototype instanceof B;
issubclass-example.js
class Animal {
}
class Cat extends Animal {
}
class AsianCat extends Cat {
}
// ------------- TEST --------------------
console.log("AsianCat === Animal? " + (AsianCat === Animal)); // false
let isSubClass1 = AsianCat === Animal || AsianCat.prototype instanceof Animal;
console.log("AsianCat is child of Animal? " + isSubClass1); // true
let isSubClass2 = AsianCat === Animal || Animal.prototype instanceof AsianCat;
console.log("Animal is child of AsianCat? " + isSubClass2); // false
Output:
isinstance('abc', object): True
ininstance(123, object): True
b = B()
a = A()
isinstance(b, A): True
isinstance(b, B): True
isinstance(a, B): False
isinstance(B, A): True
isinstance(A, B): False
5. Полиморфизм с функцией
Языки Java, C# очень близки по виду данных. Поэтому когда вы вызываете функцию вам нужно передать правильный вид данных соответствующие параметрам. В ECMAScript при вызове функции вы можете передать параметр с любым видом данных, может быть риск, и программист должен сам управлять этими рисками.
Здесь я создаю два класса, English и French. У обоих классов есть метод greeting(). Оба создают разные приветствия. Создайте два соответствующих объекта из двух классов выше и вызовите действия этих двух объектов в одной функции (функция intro).
polymorphism-example.js
class English {
greeting() {
console.log("Hello");
}
}
class French {
greeting() {
console.log("Bonjour");
}
}
function intro(language) {
language.greeting();
}
// -------------- TEST ----------------------
let flora = new English();
let aalase = new French();
// Call function:
intro(flora);
intro(aalase);
let someObject = {};
intro(someObject);// Error!!!
Запуск примера:
OK, изменить код выше, добавить нужные проверки чтобы избежать риски для вашего компьютера:
polymorphism-example2.js
class English {
greeting() {
console.log("Hello");
}
}
class French {
greeting() {
console.log("Bonjour");
}
}
function intro(language) {
// Check type of 'language' object.
if(language instanceof English || language instanceof French) {
language.greeting();
}
}
// -------------- TEST ----------------------
let flora = new English();
let aalase = new French();
// Call function:
intro(flora);
intro(aalase);
let someObject = {};
intro(someObject);
Применить архитектуру "Класс & Наследствие" помогает легче управлять ваше приложение и избежать ошибки.
polymorphism-example3.js
// A Base class
class Language {
}
class English extends Language {
greeting() {
console.log("Hello");
}
}
class French extends Language {
greeting() {
console.log("Bonjour");
}
}
function intro(language) {
// Check type of 'language' object.
if(language instanceof Language) {
language.greeting();
}
}
// -------------- TEST ----------------------
let flora = new English();
let aalase = new French();
// Call function:
intro(flora);
intro(aalase);
let someObject = {};
intro(someObject);
Смотрите так же:
Pуководства ECMAScript, Javascript
- Введение в Javascript и ECMAScript
- Быстрый старт с Javascript
- Диалоговое окно Alert, Confirm, Prompt в Javascript
- Быстрый запуск с JavaScript
- Переменные (Variable) в JavaScript
- Битовые операции
- Массивы (Array) в JavaScript
- Циклы в JavaScript
- Руководство JavaScript Function
- Руководство JavaScript Number
- Руководство JavaScript Boolean
- Руководство JavaScript String
- Заявление if/else в JavaScript
- Заявление Switch в JavaScript
- Обработка ошибок в JavaScript
- Руководство JavaScript Date
- Руководство JavaScript Module
- Функция setTimeout и setInterval в JavaScript
- Руководство Javascript Form Validation
- Руководство JavaScript Web Cookie
- Ключевое слово void в JavaScript
- Классы и объекты в JavaScript
- Техника симулирования класса и наследственности в JavaScript
- Наследование и полиморфизм в JavaScript
- Понимание Duck Typing в JavaScript
- Руководство JavaScript Symbol
- Руководство JavaScript Set Collection
- Руководство JavaScript Map Collection
- Понимание JavaScript Iterable и Iterator
- Руководство Регулярное выражение JavaScript
- Руководство JavaScript Promise, Async Await
- Руководство Javascript Window
- Руководство Javascript Console
- Руководство Javascript Screen
- Руководство Javascript Navigator
- Руководство Javascript Geolocation API
- Руководство Javascript Location
- Руководство Javascript History API
- Руководство Javascript Statusbar
- Руководство Javascript Locationbar
- Руководство Javascript Scrollbars
- Руководство Javascript Menubar
- Руководство JavaScript JSON
- Обработка событий в Javascript
- Руководство Javascript MouseEvent
- Руководство Javascript WheelEvent
- Руководство Javascript KeyboardEvent
- Руководство Javascript FocusEvent
- Руководство Javascript InputEvent
- Руководство Javascript ChangeEvent
- Руководство Javascript DragEvent
- Руководство Javascript HashChangeEvent
- Руководство Javascript URL Encoding
- Руководство Javascript FileReader
- Руководство Javascript XMLHttpRequest
- Руководство Javascript Fetch API
- Разбор XML в Javascript с помощью DOMParser
- Введение в Javascript HTML5 Canvas API
- Выделение кода с помощью библиотеки Javascript SyntaxHighlighter
Show More