Pseudoklassische Vererbung in JavaScript – Teil 2
Note: This post is from my old blog and thus written in German and potentially obsolete.
Nach dem ersten Teil der kleinen Reihe zum Thema pseudoklassische Vererbung in JavaScript, enthält dieser Teil die Muster 2 und 3 aus Stoyan Stefanovs Buch ["JavaScript Patterns"](https:// books.google.de/books?id=WTZqecc9olUC&printsec=frontcover&dq=stefanov+javascript+patterns&hl=de&sa=X&ved=0ahUKEwj34oLMj8jpAhXdB50JHWp1Aj4Q6AEIKzAA#v=onepage&q=stefanov%20javascript%20patterns&f=false).
2. Einen Konstruktor leihen
Das letztes Mal hier vorgestellte Standardmuster hatte zum einen den Nachteil, dass sowohl Eigenschaften von this
als auch Eigenschaften des Prototyps an Kind-Objekte vererbt werden. Dies wird durch das zweite Muster behoben. Dabei wird der Eltern-Konstruktor "geliehen" und diesem das Kind-Objekt sowie alle beim Aufruf des Kind-Konstruktors verwendeten Argumente übergeben. "Leihen" weißt darauf hin, dass der Eltern-Konstruktor nicht direkt mit new
sondern mit Hilfe von apply
aufgerufen wird:
function Child(a, b, c, d) {
Parent.apply(this, arguments);
}
Hier gibt es also keine direkte Verbindung zwischen Parent
und Child
, da für die Vererbung nicht mehr Child.prototype
zur Anwendung kommt. Daher vererbt man nur Eigenschaften welche this
im Eltern-Konstruktor hinzugefügt wurden. Außerdem erhalten die Kind-Objekte Kopien der vererbten Eigenschaften. Beim Standardmuster hingegen bekommen sie lediglich Referenzen, weshalb es Kindern möglich ist Eltern-Eigenschaften zu überschreiben.
Hier das vom Standardmuster bekannte Beispiel mit angepasstem Eltern-Konstruktor:
// Eltern-Konstruktor
function Parent(name) {
this.name = name || 'Adam';
}
// Funktionalität wird dem Prototyp hinzugefügt (aber nicht nicht mehr vererbt)
Parent.prototype.say = function () {
return this.name;
};
// Kind-Konstruktor
function Child(name) {
Parent.apply(this, arguments);
}
var kid = new Child('Patrick');
kid.name; // "Patrick"
typeof kid.say; // "undefined"
Mehrfachvererbung mit Hilfe von geliehenen Konstruktoren
Durch Leihen von mehrere Konstruktoren kann man eine Mehrfachvererbung erreichen. Dies sollte in der Praxis jedoch vermieden werden, da es zu Problemen führen kann.
function Cat() {
this.legs = 4;
this.say = function () {
return 'meaowww';
};
}
function Bird() {
this.wings = 2;
this.fly = true;
}
function CatWings() {
Cat.apply(this);
Bird.apply(this);
}
var jane = new CatWings();
console.log(jane.legs); // 4
console.log(jane.wings); // 2
Vor- und Nachteile des geliehenen Konstruktors
Ein Vorteil dieses Musters ist, dass man echte Kopien der Eigenschaften des Eltern-Konstrukors bekommt anstatt nur Referenzen. Ein unbeabsichtigtes Überschreiben ist somit nicht mehr möglich.
Der Nachteil ist offensichtlich, dass keine Vererbung der Eigenschaften die dem Prototyp hinzugefügt werden mehr stattfindet. Wiederverwendebare Eigenschaften sollen jedoch dem Prototyp hinzugefügt und auch vererbt werden. Das folgende Muster behebt diesen Nachteil daher.
3. Einen Konstruktor leihen und den Protoyp festlegen
Dieses Muster kombiniert einfach die zwei vorhergehenden. Dies geschieht in dem zusätzlich zur Verwendung des geliehenen Konstuktors dem Protoyp von Child
eine neue Instanz von Parent
zugewiesen wird:
function Child(a, b, c, d) {
Parent.apply(this, arguments);
}
Child.prototype = new Parent();
Das Kind kann nun auch Aurgumente an den Eltern-Konstruktor übergeben. Allerdings wir dieser nun zweimal aufgerufen, wodurch seine Eigenschaften sozusagen doppelt vererbt werden.
// Eltern-Konstruktor
function Parent(name) {
this.name = name || 'Adam';
}
// Funktionalitat wird dem Prototyp hinzugefugt (und jetzt wieder vererbt)
Parent.prototype.say = function () {
return this.name;
};
// Kind-Konstuktor und setzen des Prototyps
function Child(name) {
Parent.apply(this, arguments);
}
Child.prototype = new Parent();
var kid = new Child('Patrick');
kid.name; // "Patrick"
kid.say(); // "Patrick"
Wie man sieht, wird say
jetzt korrekt vererbt. Durch die doppelte Vererbung kann man zudem den an den Kind-Konstruktor übergebenen Namen löschen und erhält dann bei Aufruf von say
wieder den im Eltern-Konstruktor zugewiesenen Wert:
delete kid.name;
kid.say(); // "Adam"