В этом руководстве вы узнаете, как реализовать наследование 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(); // flyingES6 упростил эти шаги, используя ключевые слова 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для вызова методов родительского класса в методах дочернего класса.
