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