Rolf b: Frage zum Wiki-Artikel ‚new‘ - Prototypenbeschreibung

Beitrag lesen

problematische Seite

Ja, grundsätzlich schon. Ich hätte aber noch den Hinweis, dass man Konstruktorfunktionen per Konvention groß schreibt, um sie von normalen Funktionen zu unterscheiden.

Der entscheidende Punkt ist, dass jedes Objekt - wirklich jedes - ein verborgenes Property für den Prototyp hat. Dieses verborgene Property darf man übrigens NICHT mit der prototype-Eigenschaft einer Konstruktorfunktion verwechseln!

Manche Laufzeitumgebungen stellen dieses Property als __proto__ bereit - das ist aber non-standard. Der offizielle Weg zum Prototypen ist Object.getPrototypeOf(x). Und wenn man dann das Property foo des Objekts, das in bar steht, LESEN will, beginnt die JS Laufzeitumgebung mit der Suche.

  • Ist 'foo' in bar vorhanden?
  • Ist 'foo' in Object.getPrototypeOf(bar) vorhanden?
  • Ist 'foo' in Object.getPrototypeOf(Object.getPrototypeOf(bar)) vorhanden?

und so weiter. Da Methoden nichts weiter sind als Funktionen, die als Eigenschaft eines Objekts gespeichert sind, funktioniert diese Suchkette für Datenproperties genauso wie für Methoden.

Wenn man ein Property schreiben will, wird immer nur am Objekt selbst geschrieben. Durch Operationen am Objekt selbst wird der Prototyp nicht verändert.

Nun stellt sich die Frage, wo der Wert herkommt, der im Prototyp in der property-Eigenschaft eines Objekts steht. Wird ein Objekt neu erzeugt, kann man den Prototypen explizit angeben:

var a = { foo: 17 };
var b = Object.create(a);
/// Object.getPrototypeOf(b) === a ---> true

a ist jetzt der Prototyp für b. a selbst hat auch einen Prototypen, und zwar das Objekt, das in Object.prototype zu finden ist. DIESES Objekt ist wiederum das einzige Objekt in der JavaScript-Objektsuppe, dessen Prototyp leer ist (genauer gesagt: das interne Prototyp-Property enthält den Wert null).

Object.create nutzt man aber eher selten. Im Normalfall verwendet man den new Operator. Oder ein Objektliteral, was aber Syntaxzucker ist für new Object(). Den new-Operator kannst Du Dir vereinfacht vorstellen als

var newObject = Object.create(Constructor.prototype);
Constructor.apply(newObject, constructorArgs);
return newObject;

Diese Vereinfachung lässt einiges außer Acht, vor allem den Rückgabewert der Konstruktorfunktion. Die Details stehen in der JavaScript-Spec, viel Spaß beim Decodieren...

Wenn Du eine eigene Funktion Bar erzeugst, bekommt sie von JavaScript automatisch eine neues Objekt als prototype-Property zugewiesen. Dieses Prototypobjekt kannst Du erweitern, kannst es aber auch komplett überschreiben. Benutzt Du dann new Bar(), so bekommt das erzeugte Objekt den Prototypen Bar.prototype. Aber nicht vergessen, Bar.prototype ist NICHT der Prototyp von Bar.

// Object.getPrototypeOf(Object) === Object.prototype -> false
// Object.getPrototypeOf(Object) === Function.prototype -> true!

var a = { foo:17 };
// Object.getPrototypeOf(a) === Object.prototype -> true

var b = Object.create(a);
// Object.getPrototypeOf(a) === a -> true

function Bar = function() { };
Bar.prototype = { baz:99 };
var c = new Bar();
// Object.getPrototypeOf(c) === Bar.prototype -> true
// Object.getPrototypeOf(c) === Object.getPrototypeOf(Bar) -> false
// Object.getPrototypeOf(Bar) === Object.getPrototypeOf(Function) -> true

Interessant ist auch die Frage, ob es sinnvoller ist, Methoden am Prototyp zu definieren oder sie in der Konstruktorfunktion als Eigenschaften zuzuweisen. Beides geht, aber der Effekt ist unterschiedlich.

  • Weise ich sie in der Konstruktorfunktion zu, passiert das bei jedem new-Aufruf. Muss ich viele Objekte zu dieser Konstruktorfunktion erzeugen, kostet das deutlich Zeit. Stehen die Methoden am Prototyp, geschieht das nur einmal und damit schneller
  • Andererseits kann ich in Methoden, die in der Konstruktorfunktion definiert werden, auf lokale Variablen der Konstruktorfunktion zugreifen und damit echt private Objekteigenschaften realisieren. Methoden, die auf dem Prototypobjekt definiert sind, können das nicht.
  • Füge ich den Prototyp eine Eigenschaft oder eine Methode hinzu, gilt diese sofort für alle Objekte, die diesen Prototypen verwenden.

Das class Keyword von ECMAScript 6 ist übrigens nur Syntaxzucker für eine Konstruktorfunktion.

So, ich hoffe, dass ich jetzt alle Klarheiten endgültig beseitigt habe. Es steht auch alles im WIKI, aber manchmal hilft auch eine andere Formulierung des gleichen Sachverhalts :)

Rolf