Наследование и полиморфизм в ECMAScript

1- Наследование в ECMAScript

Перед изучением " Наследование в ECMAScript", удостоверьтесь, что у вас уже есть представление о " К лассе и объекте", если нет, давайте изучим:
ECMAScript позволяет вам создать расширенный класс из одного или многих других классов. Этот класс называется производный класс (derived class) или просто подкласс .
Подкласс унаследует  property, методы и другие члены от родительского класса. Он так же может переопределить(override) методы из родительского класса. В  ECMAScript каждый класс имеет только 1 единственный constructor. Если класс сам не определяет свой собственный  constructor, он унаследует  constructor родительского класс. Наоборот, если подкласс определяет свой constructor, то не унаследует  constructor родительского класса.
В отличие от Java, CSharp и некоторых других языков, ECMAScript допускает множественное наследование. Класс может быть расширен из одного или нескольких родительских классов.
Нам нужно несколько классов для примера.
  • Animal (Животное): Класс имитирует животное.
  • Duck (Утка): Класс имитирует утку, это подкласс Животного.
  • Cat (Кот): Класс имитирует кота, это подкласс Животного.
  • Mouse (Мышь): Класс имитирует мышь, это подкласс Животного.
*************** (KT lai)
В ECMAScript, конструктор ( constructor) используется для создания объекта и прикрепляет значение property (свойства).
Конструктор подклассов всегда вызывается конструктором родительского класса, чтобы инициализировать значение для property родительского класса, затем он прикрепляет значение этим атрибутам.
Например:
*************** (KT lai)
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();
Что случится, когда вы создаёте объект конструктором ( 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();

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 используется для проверки вида "чего-то", полученный результат будет название вида.
Type Result
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

 

5- Полиморфизм с функцией

Языки  Java, C# очень близки по виду данных. Поэтому когда вы вызываете функцию (Hàm) вам нужно передать правильный вид данных соответствующие параметрам. В 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);
Смотрите так же: