A herança é uma forma de criar relações pai-filho entre classes. A classe filha (por vezes chamada de subclasse) tem acesso ao comportamento e aos dados definidos pela classe pai (por vezes chamada de superclasse).
class Pet {
constructor(name) {
this.name = name;
}
introduce() {
console.log(`This is my pet, ${this.name}.`);
}
}
class Dog extends Pet {}
const dog = new Dog('Otis');
dog.introduce();
// => This is my pet, Otis.
A palavra-chave extends na declaração da classe filha estabelece uma relação com a classe pai através da cadeia de protótipos.
Os objetos criados pelo construtor da classe filha têm o protótipo da classe pai na sua cadeia de protótipos, o que dá acesso a todos os métodos ou dados definidos pela classe pai.
const dog = new Dog('Otis');
Dog.prototype.isPrototypeOf(dog); // => true
Pet.prototype.isPrototypeOf(dog); // => true
Pet.prototype.isPrototypeOf(Dog.prototype); // => true
Pet.prototype.hasOwnProperty('introduce'); // => true
Dog.prototype.hasOwnProperty('introduce'); // => false
dog.hasOwnProperty('introduce'); // => false
Se a classe filha não definir uma função construtora, é usada a função construtora da classe pai.
No entanto, se a classe filha definir uma função construtora própria, é preciso chamar explicitamente o construtor da classe pai.
Para invocar o construtor da classe pai a partir do âmbito do construtor da classe filha, usa-se a palavra-chave super.
class Pet {
constructor(name) {
this.name = name;
}
}
class Dog extends Pet {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
const dog = new Dog('Otis', 'Pug');
Como o construtor da classe pai é responsável por inicializar um novo objeto e atribuí-lo a this, tem de ser chamado antes de o construtor da classe filha usar this.
class Dog extends Pet {
constructor(name, breed) {
// using 'this' before calling the parent constructor with 'super'
this.breed = breed;
super(name);
}
}
const dog = new Dog('Otis', 'Pug');
// => ReferenceError: Must call super constructor in derived class before accessing 'this'...
Uma classe filha pode definir comportamento próprio, para além do comportamento herdado da classe pai.
Esta é uma das principais razões para usar herança: ter classes filhas especializadas, com dados e métodos próprios, relacionadas através de métodos e dados partilhados fornecidos pela classe pai.
class Dog extends Pet {
constructor(name, breed) {
super(name);
this.breed = breed;
}
describe() {
console.log(`${this.name} is a ${this.breed}.`);
}
}
const dog = new Dog('Otis', 'Pug');
dog.introduce();
dog.describe();
// => 'This is my pet, Otis.'
// => 'Otis is a Pug.'
Uma classe filha também pode substituir o comportamento de um método definido pela classe pai e trocá-lo ou ampliá-lo com o comportamento definido pela classe filha.
class Cat extends Pet {
// replacing parent class behavior
introduce() {
console.log(`This is my cat, ${this.name}.`);
}
}
class Dog extends Pet {
constructor(name, breed) {
super(name);
this.breed = breed;
}
describe() {
console.log(`${this.name} is a ${this.breed}.`);
}
// extending parent class behavior
introduce() {
super.introduce();
this.describe();
}
}
const cat = new Cat('Milo');
cat.introduce();
// => 'This is my cat, Milo.'
const dog = new Dog('Otis', 'Pug');
dog.introduce();
// => This is my pet, Otis.
// => Otis is a Pug.
Para chamar um método definido na classe pai a partir do corpo de um método com o mesmo nome na classe filha, é preciso usar a palavra-chave super para referenciar a classe pai.