Vererbung ist eine Möglichkeit, Eltern-Kind-Beziehungen zwischen Klassen zu schaffen. Die Kindklasse (manchmal auch als Unterklasse bezeichnet) hat Zugriff auf das Verhalten und die Daten, die von der Elternklasse (manchmal auch als Oberklasse bezeichnet) definiert werden.
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.
Das Schlüsselwort extends in der Deklaration der Kindklasse stellt über die Prototypkette eine Beziehung zur Elternklasse her.
Objekte, die vom Konstruktor der Kindklasse erzeugt werden, haben den Prototyp der Elternklasse in ihrer Prototypkette. Dadurch haben sie Zugriff auf alle Methoden und Daten, die von der Elternklasse definiert werden.
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
Wenn die Kindklasse keine eigene Konstruktorfunktion definiert, wird die Konstruktorfunktion der Elternklasse verwendet.
Wenn die Kindklasse jedoch eine eigene Konstruktorfunktion definiert, muss der Konstruktor der Elternklasse explizit aufgerufen werden.
Um den Konstruktor der Elternklasse aus dem Gültigkeitsbereich des Konstruktors der Kindklasse aufzurufen, wird das Schlüsselwort super verwendet.
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');
Da der Konstruktor der Elternklasse dafür verantwortlich ist, ein neues Objekt zu initialisieren und es this zuzuweisen, muss er aufgerufen werden, bevor this vom Konstruktor der Kindklasse verwendet wird.
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'...
Eine Kindklasse kann zusätzlich zum geerbten Verhalten der Elternklasse eigenes Verhalten definieren.
Das ist einer der Hauptgründe für Vererbung: spezialisierte Kindklassen mit eigenen Daten und Methoden, die über gemeinsame Methoden und Daten der Elternklasse miteinander verbunden sind.
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.'
Eine Kindklasse kann auch das Verhalten einer von der Elternklasse definierten Methode überschreiben und es durch eigenes Verhalten ersetzen oder erweitern.
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.
Um eine in der Elternklasse definierte Methode aus dem Rumpf einer gleichnamigen Methode der Kindklasse aufzurufen, muss mit dem Schlüsselwort super auf die Elternklasse verwiesen werden.