Prototypen sind eine Optimierung .
Ein gutes Beispiel für eine gute Verwendung ist die jQuery-Bibliothek. Jedes Mal, wenn Sie ein jQuery-Objekt mithilfe von erhalten $('.someClass')
, verfügt dieses Objekt über Dutzende von "Methoden". Die Bibliothek könnte dies erreichen, indem sie ein Objekt zurückgibt:
return {
show: function() { ... },
hide: function() { ... },
css: function() { ... },
animate: function() { ... },
// etc...
};
Dies würde jedoch bedeuten, dass jedes jQuery-Objekt im Speicher immer wieder Dutzende von benannten Slots enthält, die dieselben Methoden enthalten.
Stattdessen werden diese Methoden in einem Prototyp definiert und alle jQuery-Objekte "erben" diesen Prototyp, um alle diese Methoden zu sehr geringen Laufzeitkosten zu erhalten.
Ein äußerst wichtiger Teil dessen, wie jQuery es richtig macht, ist, dass dies vor dem Programmierer verborgen ist. Es handelt sich lediglich um eine Optimierung, nicht um etwas, über das Sie sich bei der Verwendung der Bibliothek Sorgen machen müssen.
Das Problem mit JavaScript besteht darin, dass der Aufrufer bei nackten Konstruktorfunktionen daran denken muss, ihnen ein Präfix voranzustellen, new
da sie sonst normalerweise nicht funktionieren. Dafür gibt es keinen guten Grund. jQuery macht es richtig, indem es diesen Unsinn hinter einer normalen Funktion versteckt $
, sodass Sie sich nicht darum kümmern müssen, wie die Objekte implementiert werden.
Damit Sie bequem ein Objekt mit einem bestimmten Prototyp erstellen können, enthält ECMAScript 5 eine Standardfunktion Object.create
. Eine stark vereinfachte Version würde folgendermaßen aussehen:
Object.create = function(prototype) {
var Type = function () {};
Type.prototype = prototype;
return new Type();
};
Es kümmert sich nur um den Schmerz, eine Konstruktorfunktion zu schreiben und sie dann mit aufzurufen new
.
Wann würden Sie Prototypen vermeiden?
Ein nützlicher Vergleich ist mit gängigen OO-Sprachen wie Java und C #. Diese unterstützen zwei Arten der Vererbung:
- Schnittstelle Vererbung, wo Sie
implement
ein , interface
so dass die Klasse seine eigene einzigartige Implementierung für jedes Mitglied der Schnittstelle zur Verfügung stellt.
- Implementierungsvererbung , bei der Sie
extend
eine class
Standardimplementierung einiger Methoden bereitstellen.
In JavaScript ist die prototypische Vererbung eine Art Implementierungsvererbung . In Situationen, in denen Sie (in C # oder Java) von einer Basisklasse abgeleitet hätten, um ein Standardverhalten zu erhalten, an dem Sie über Überschreibungen kleine Änderungen vornehmen, ist in JavaScript eine prototypische Vererbung sinnvoll.
Wenn Sie sich jedoch in einer Situation befinden, in der Sie Schnittstellen in C # oder Java verwendet hätten, benötigen Sie in JavaScript keine bestimmte Sprachfunktion. Es ist nicht erforderlich, explizit etwas zu deklarieren, das die Schnittstelle darstellt, und Objekte müssen nicht als "Implementierung" dieser Schnittstelle markiert werden:
var duck = {
quack: function() { ... }
};
duck.quack(); // we're satisfied it's a duck!
Mit anderen Worten, wenn jeder "Typ" eines Objekts seine eigenen Definitionen der "Methoden" hat, hat das Erben von einem Prototyp keinen Wert. Danach hängt es davon ab, wie viele Instanzen Sie jedem Typ zuweisen. In vielen modularen Designs gibt es jedoch nur eine Instanz eines bestimmten Typs.
Tatsächlich wurde von vielen Leuten vorgeschlagen, dass die Vererbung von Implementierungen böse ist . Das heißt, wenn es einige allgemeine Operationen für einen Typ gibt, ist es vielleicht klarer, wenn sie nicht in eine Basis- / Superklasse eingefügt werden, sondern nur als normale Funktionen in einem Modul verfügbar gemacht werden, an das Sie die Objekte übergeben. Sie möchten, dass sie operieren.