Vererbung überdacht

Unter Vererbung und der Prototype des Konstruktors findet man eine Beschreibung zur Vererbung bei JavaScript und dem Prototype des Konstruktors.

Vererbung war von Anfang bei JavaScript vorhanden. Die Beispiele auf dieser Seite setzen jedoch einige Methoden ein, die erst mit ECMAScript 5 eingeführt wurden. Auf den entsprechenden Referenzseiten für die Methoden kann man herausfinden, ob diese sich für ältere Versionen emulieren lassen.

Beispiel

B soll von A erben:

function A(a){
  this.varA = a;
}

A.prototype = {
  varA : null,
  doSomething : function(){
    // ...
  }
}

function B(a, b){
  A.call(this, a);
  this.varB = b;
}
B.prototype = Object.create(A.prototype, {
  varB : {
    value: null, 
    enumerable: true, 
    configurable: true, 
    writable: true 
  },
  doSomething : { 
    value: function(){ // override
      A.prototype.doSomething.apply(this, arguments); // call super
      // ...
    },
    enumerable: true,
    configurable: true, 
    writable: true
  }
});

var b = new B();
b.doSomething();

Die wichtigen Teile sind:

  • Typen sind in .prototype definiert
  • Die Methode Object.create()wird zum Vererben benutzt

prototype und Object.getPrototypeOf

JavaScript ist ein wenig verwirrend für Entwickler, die von Java oder C++ kommen, da bei JavaScript alles dynamisch und laufzeitbasiert ist und überhaupt keine Klassen existieren. Alles sind nur Instanzen (Objekte). Sogar die "Klassen", die wir simulieren, sind nur function-Objekte.

Vielleicht haben Sie schon bemerkt, dass die Funktion A eine spezielle Eigenschaft prototype besitzt. Diese spezielle Eigenschaft arbeitet mit dem Operator new von JavaScript. Die Referenz auf das Prototype-Objekt wird für die interne Eigenschaft [[Prototype]] der neuen Instanz kopiert. Bei der Anweisung var a1 = new A() setzt JavaScript (nachdem das Objekt im Speicher erstellt und bevor die Funktion A() aufgerufen und this zugewiesen wurde) z. B. a1.[[Prototype]] = A.prototype. Beim Zugriff auf Eigenschafen der Instanz überprüft JavaScript dann zuerst, ob diese Eigenschaft direkt existiert und wenn nicht wird in [[Prototype]] gesucht. Das bedeutet, dass alles, was für prototype definiert wird, tatsächlich von allen Instanzen geteilt wird. Später kann man Teile von prototype verändern und diese Änderungen werden daraufhin, wenn man so will, für alle Instanzen übernommen.

Bei der Anweisung var a1 = new A(); var a2 = new A(); aus dem Beispiel oben, verweist a1.doSomething auf Object.getPrototypeOf(a1).doSomething, was dasselbe ist wie A.prototype.doSomething, das definiert wurde, z. B. Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething.

Kurz: prototype ist für Typen und Object.getPrototypeOf() dasselbe für Instanzen.

[[Prototype]] wird rekursiv durchlaufen, z. B.  a1.doSomething, Object.getPrototypeOf(a1).doSomething, Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething usw. bis etwas gefunden wurde. Falls nichts gefunden wurde, gibt Object.getPrototypeOf den Wert null zurück.

Beim Aufruf:

var o = new Foo();

macht JavaScript also folgendes:

var o = new Object();
o.[[Prototype]] = Foo.prototype;
o.Foo();

(oder ähnliches) und wird später diese Anweisung ausgeführt:

o.someProp;

Dann wird erst überprüft, ob o die Eigenschaft someProp besitzt. Wenn nicht, wird Object.getPrototypeOf(o).someProp überprüft und wenn die Eigenschaft auch dann nicht gefunden wurde, wird Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp überprüft. Dies wird solange fortgesetzt, bis die Eigenschaft entweder gefunden oder festgestellt wurde, dass sie nicht existert.