Наследование JavaScript: ключевые слова extends и super

В этом руководстве вы узнаете, как реализовать наследование JavaScript с помощью ключевых слов extends и super в ES6.

До ES6 реализация правильного наследования требовала нескольких шагов. Одной из наиболее часто используемых стратегий является прототипное наследование.

Ниже показано, как Bird наследует свойства от Animal, используя технику прототипного наследования:

function Animal(legs) {
    this.legs = legs;
}
Animal.prototype.walk = function() {
    console.log('walking on ' + this.legs + ' legs');
}
function Bird(legs) {
    Animal.call(this, legs);
}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Animal;
Bird.prototype.fly = function() {
    console.log('flying');
}
var pigeon = new Bird(2);
pigeon.walk(); // walking on 2 legs 
pigeon.fly(); // flying

ES6 упростил эти шаги, используя ключевые слова extends и super.

В следующем примере определяются классы Animal и Bird и устанавливается наследование с помощью ключевых слов extends и super.

class Animal {
    constructor(legs) {
        this.legs = legs;
    }
    walk() {
        console.log('walking on ' + this.legs + ' legs');
    }
}
class Bird extends Animal {
    constructor(legs) {
        super(legs);
    }
    fly() {
        console.log('flying');
    }
}
let bird = new Bird(2);
bird.walk();
bird.fly();

Как это работает:

  • Во-первых, используйте ключевое слово extends, чтобы сделать класс Bird наследником класса Animal :
class Bird extends Animal { 
    // ... 
}

Класс Animal называется базовым классом или родительским классом, а класс Bird известен как производный класс или дочерний класс. При этом класс Bird наследует все методы и свойства класса Animal.

  • Во-вторых, в конструкторе Bird вызовите super(), чтобы затем вызвать конструктор Animal с аргументом legs.

JavaScript требует, чтобы дочерний класс вызывал super(), если у него есть конструктор. Как вы можете видеть в классе Bird, оператор super(legs) эквивалентен следующему оператору в ES5:

Animal.call(this, legs);

Если у класса Bird нет конструктора, вам больше ничего не нужно делать:

class Bird extends Animal {
    fly() {
        console.log('flying');
    }
}

Он эквивалентен следующему классу:

class Bird extends Animal {
    constructor(...args) {
        super(...args);
    }
    fly() {
        console.log('flying');
    }
}

Однако у дочернего класса есть конструктор, ему нужно вызвать super(). Например, следующий код приводит к ошибке:

class Bird extends Animal {
    constructor(legs) {}
    fly() {
        console.log('flying');
    }
}

Ошибка:

ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

Поскольку super() инициализирует this объект, вам необходимо вызвать super() перед доступом к this объекту. Попытка получить доступ к this перед вызовом super() также приводит к ошибке.

Например, если вы хотите инициализировать свойство color класса Bird, вы можете сделать это следующим образом:

class Bird extends Animal {
    constructor(legs, color) {
        super(legs);
        this.color = color;
    }
    fly() {
        console.log("flying");
    }
    getColor() {
        return this.color;
    }
}
let pegion = new Bird(2, "white");
console.log(pegion.getColor());

Методы затенения

ES6 позволяет дочернему и родительскому классам иметь методы с одинаковыми именами. В этом случае, когда вы вызываете метод объекта дочернего класса, метод дочернего класса будет скрывать метод родительского класса.

Следующий класс Dog расширяет класс Animal и переопределяет метод walk() :

class Dog extends Animal {
    constructor() {
        super(4);
    }
    walk() {
        console.log(`go walking`);
    }
}
let bingo = new Dog();
bingo.walk(); // go walking

Чтобы вызвать метод родительского класса в дочернем классе, вы используете super.method(arguments) следующим образом:

class Dog extends Animal {
    constructor() {
        super(4);
    }
    walk() {
        super.walk();
        console.log(`go walking`);
    }
}
let bingo = new Dog();
bingo.walk();
// walking on 4 legs 
// go walking

Наследование статических членов

Помимо свойств и методов дочерний класс также наследует все статические свойства и методы родительского класса. Например:

class Animal {
    constructor(legs) {
        this.legs = legs;
    }
    walk() {
        console.log('walking on ' + this.legs + ' legs');
    }
    static helloWorld() {
        console.log('Hello World');
    }
}
class Bird extends Animal {
    fly() {
        console.log('flying');
    }
}

В этом примере класс Animal имеет статический метод helloWorld(), и этот метод доступен как Bird.helloWorld() и ведет себя так же, как метод Animal.helloWorld() :

Bird.helloWorld();
  // Hello World

Наследование от встроенных типов

JavaScript позволяет расширять встроенные типы, такие как Array, String, Map и Set, посредством наследования.

Следующий класс Queue расширяет ссылочный тип Array. Синтаксис намного чище, чем у Queue, реализованной с использованием шаблона конструктор/прототип.

class Queue extends Array {
    enqueue(e) {
        super.push(e);
    }
    dequeue() {
        return super.shift();
    }
    peek() {
        return ! this.empty() ? this[0] : undefined;
    }
    empty() {
        return this.length === 0;
    }
}
var customers = new Queue();
customers.enqueue('A');
customers.enqueue('B');
customers.enqueue('C');
while (!customers.empty()) {
    console.log(customers.dequeue());
}

Заключение

  • Используйте ключевое слово extends для реализации наследования в ES6. Расширяемый класс называется базовым классом или родительским классом. Класс, который расширяет базовый класс или родительский класс, называется производным классом или дочерним классом.
  • Вызовите super(arguments) в конструкторе дочернего класса, чтобы вызвать конструктор родительского класса.
  • Используйте ключевое слово super для вызова методов родительского класса в методах дочернего класса.
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript