betacode

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

  1. Наследование в ECMAScript
  2. Переопределить метод
  3. Абстрактный метод
  4. Операторы instanceof, typeof
  5. Полиморфизм с функцией

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

Show More