Prototypen in JavaScript

Wenn Sie eine Funktion in JavaScript definieren, werden einige vordefinierte Eigenschaften bereitgestellt. Einer davon ist der illusorische Prototyp. In diesem Artikel werde ich detailliert beschreiben, was es ist und warum Sie es in Ihren Projekten verwenden sollten.


Was ist Prototyp??

Die Prototypeneigenschaft ist anfangs ein leeres Objekt und kann - wie jedes andere Objekt - Mitglieder hinzugefügt werden.

var meinObjekt = Funktion (Name) this.name = Name; kehre das zurück; ; console.log (Typ von myObject.prototype); // object myObject.prototype.getName = function () return this.name; ;

Im obigen Snippet haben wir eine Funktion erstellt, die wir jedoch aufrufen myObject (), es wird einfach das zurückgeben Fenster Objekt, weil es im globalen Bereich definiert wurde. diese gibt daher das globale Objekt zurück, da es noch nicht instanziiert wurde (dazu später mehr).

console.log (meinObject () === Fenster); // wahr

Die geheime Verbindung

Jedes Objekt in JavaScript hat eine "geheime" Eigenschaft.

Bevor wir fortfahren, möchte ich die "geheime" Verbindung diskutieren, die Prototypen so funktionieren lässt, wie es funktioniert.

Jedem Objekt in JavaScript wird eine "geheime" Eigenschaft hinzugefügt, wenn es definiert oder instanziiert wird __proto__; So wird auf die Prototypkette zugegriffen. Es ist jedoch keine gute Idee, darauf zuzugreifen __proto__ in Ihrer Anwendung, da es nicht in allen Browsern verfügbar ist.

Das __proto__ property sollte nicht mit dem Prototyp eines Objekts verwechselt werden, da es sich um zwei separate Eigenschaften handelt. Das heißt, sie gehen Hand in Hand. Es ist wichtig, diese Unterscheidung zu treffen, da dies anfangs recht verwirrend sein kann! Was heißt das genau? Lassen Sie mich erklären. Als wir die erstellt haben meinObjekt Funktion definieren wir ein Objekt vom Typ Funktion.

console.log (Typ meinesObjekts); // Funktion

Für diejenigen, die es nicht wissen, Funktion ist ein vordefiniertes Objekt in JavaScript und hat daher seine eigenen Eigenschaften (z. Länge und Argumente) und Methoden (z. Anruf und sich bewerben). Und ja, es hat auch ein eigenes Prototypobjekt sowie das Geheimnis __proto__ Verknüpfung. Dies bedeutet, dass sich irgendwo in der JavaScript-Engine etwas Code befindet, der dem folgenden ähnelt:

 Function.prototype = Argumente: null, Länge: 0, Aufruf: function () // Geheimcode, zutreffend: Funktion () // Geheimcode…

In Wahrheit wäre es wahrscheinlich nicht so simpel; Dies soll lediglich veranschaulichen, wie die Prototypkette funktioniert.

Also haben wir definiert meinObjekt als Funktion und ein Argument gegeben, Name; Wir legen jedoch keine Eigenschaften fest, wie z Länge oder Methoden, wie z Anruf. Warum funktioniert das Folgende??

console.log (meinObjekt.length); // 1 (Anzahl der verfügbaren Argumente)

Dies liegt daran, wenn wir definiert haben meinObjekt, es schuf ein __proto__ Eigenschaft und setzen Sie den Wert auf Funktionsprotokoll (veranschaulicht im obigen Code). Also, wenn wir zugreifen myObject.length, es sucht eine Eigenschaft von meinObjekt namens Länge und findet keine; es fährt dann über die Kette die Kette hinauf __proto__ link, findet die Eigenschaft und gibt sie zurück.

Sie fragen sich vielleicht warum Länge ist eingestellt auf 1 und nicht 0 - oder eine andere Nummer für diese Tatsache. Das ist weil meinObjekt ist in der Tat ein Beispiel von Funktion.

console.log (myObject-Instanz von Function); // true console.log (myObject === Funktion); // falsch

Wenn eine Instanz eines Objekts erstellt wird, wird die __proto__ Diese Eigenschaft wird aktualisiert, um auf den Prototyp des Konstruktors zu verweisen, der in diesem Fall ist Funktion.

console.log (meinObjekt .__ proto__ === Function.prototype) // true

Darüber hinaus beim Erstellen eines neuen Funktion Objekt, der native Code im Funktion Der Konstruktor zählt die Anzahl der Argumente und die Aktualisierung diese Länge entsprechend, was in diesem Fall ist 1.

Wenn wir jedoch eine neue Instanz von erstellen meinObjekt Verwendung der Neu Stichwort, __proto__ wird auf zeigen myObject.prototype wie meinObjekt ist der Konstruktor unserer neuen Instanz.

 var myInstance = neues myObject ("foo"); console.log (meineInstanz .__ proto__ === meinObjekt.prototyp); // wahr

Zusätzlich zum Zugriff auf die systemeigenen Methoden innerhalb der Funktion.Prototyp, wie z Anruf und sich bewerben, Wir haben jetzt Zugriff auf meinObjektMethode, getName.

 console.log (myInstance.getName ()); // foo var mySecondInstance = new myObject ("bar"); console.log (mySecondInstance.getName ()); // bar console.log (myInstance.getName ()); // foo

Wie Sie sich vorstellen können, ist dies sehr praktisch, da es dazu verwendet werden kann, ein Objekt zu entwerfen und so viele Instanzen wie nötig zu erstellen. Dies führt mich zum nächsten Thema!


Warum ist die Verwendung von Prototypen besser??

Nehmen wir beispielsweise an, wir entwickeln ein Canvas-Spiel und benötigen mehrere (möglicherweise hunderte) Objekte gleichzeitig auf dem Bildschirm. Jedes Objekt benötigt seine eigenen Eigenschaften, wie z x und y Koordinaten, Breite,Höhe, und viele andere.

Wir könnten es wie folgt machen:

 var GameObject1 = x: Math.floor ((Math.random () * myCanvasWidth) + 1), y: Math.floor ((Math.random () * myCanvasHeight) + 1), Breite: 10, Höhe: 10, draw: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height);  ...; var GameObject2 = x: Math.floor ((Math.random () * myCanvasWidth) + 1), y: Math.floor ((Math.random () * myCanvasHeight) + 1), Breite: 10, Höhe: 10, draw: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height);  ...;

… Mach das noch 98 mal…

Dazu werden alle diese Objekte im Speicher erstellt - alle mit separaten Definitionen für Methoden, wie z zeichnen und was auch immer andere Methoden erforderlich sind. Dies ist sicherlich nicht ideal, da das Spiel die dem Browser zugewiesenen JavaScript-Speicher aufblähen wird und es sehr langsam laufen wird ... oder gar nicht mehr reagiert.

Während dies bei nur 100 Objekten wahrscheinlich nicht der Fall ist, kann dies dennoch zu einem echten Performance-Hit werden, da mehr als 100 einzelne Objekte nachgesehen werden müssen Prototyp Objekt.


So verwenden Sie den Prototyp

Um die Anwendung schneller laufen zu lassen (und Best Practices zu folgen), können wir die Prototypeneigenschaft (neu) definieren GameObject; jede Instanz von GameObject referenziert dann die Methoden innerhalb von GameObject.prototyp als ob sie ihre eigenen Methoden wären.

 // Definiere die GameObject-Konstruktorfunktion var GameObject = Funktion (Breite, Höhe) this.x = Math.floor ((Math.random () * myCanvasWidth) + 1); this.y = Math.floor ((Math.random () * myCanvasHeight) + 1); this.width = width; this.height = Höhe; kehre das zurück; ; // Definiere das GameObject-Prototypobjekt (neu). GameObject.prototype = x: 0, y: 0, Breite: 5, Breite: 5, draw: function () myCanvasContext.fillRect (this.x, this.y, this) .breite, this.height); ;

Das GameObject kann dann 100 Mal instanziiert werden.

 var x = 100, arrayOfGameObjects = []; do arrayOfGameObjects.push (neues GameObject (10, 10));  while (x--);

Jetzt haben wir ein Array von 100 GameObjects, die alle den gleichen Prototyp und die gleiche Definition von haben zeichnen Methode, die Speicherplatz innerhalb der Anwendung drastisch einspart.

Wenn wir das anrufen zeichnen Methode referenziert es genau dieselbe Funktion.

 var GameLoop = function () für (gameObject in arrayOfGameObjects) gameObject.draw (); ;

Prototyp ist ein Live-Objekt

Der Prototyp eines Objekts ist sozusagen ein Live-Objekt. Dies bedeutet einfach, dass wir, wenn wir alle GameObject-Instanzen erstellt haben, entscheiden, anstatt ein Rechteck zu zeichnen, einen Kreis zu zeichnen, können wir unseren aktualisieren GameObject.prototype.draw Methode entsprechend.

 GameObject.prototype.draw = function () myCanvasContext.arc (this.x, this.y, this.width, 0, Math.PI * 2, true); 

Und jetzt alle früheren Instanzen von GameObject und zukünftige Instanzen werden einen Kreis zeichnen.


Native Objects-Prototypen aktualisieren

Ja das ist möglich. Sie sind möglicherweise mit JavaScript-Bibliotheken wie Prototype vertraut, die diese Methode nutzen.

Lassen Sie uns ein einfaches Beispiel verwenden:

 String.prototype.trim = function () return this.replace (/ ^ \ s + | \ s + $ / g, ");;

Wir können jetzt als Methode eines beliebigen Strings darauf zugreifen:

"Foo bar" .trim (); // "foo bar"

Es gibt jedoch einen kleinen Nachteil. Beispielsweise können Sie dies in Ihrer Anwendung verwenden. Ein oder zwei Jahre später kann ein Browser eine aktualisierte Version von JavaScript implementieren, die eine native Version enthält trimmen Methode innerhalb der StringPrototyp. Dies bedeutet, dass Ihre Definition von trimmen wird die native Version überschreiben! Yikes! Um dies zu umgehen, können Sie vor der Definition der Funktion einen einfachen Check hinzufügen.

 if (! String.prototype.trim) String.prototype.trim = function () return this.replace (/ ^ \ s + | \ s + $ / g, ");;

Wenn es vorhanden ist, wird die native Version von verwendet trimmen Methode.

Als Faustregel gilt es im Allgemeinen als bewährte Methode, um die Erweiterung nativer Objekte zu vermeiden. Aber wie immer können Regeln gebrochen werden, wenn dies erforderlich ist.


Fazit

Hoffentlich hat dieser Artikel das Rückgrat von JavaScript als Prototyp beleuchtet. Sie sollten jetzt auf dem besten Weg sein, effizientere Anwendungen zu erstellen.

Wenn Sie Fragen zum Prototyp haben, lassen Sie es mich in den Kommentaren wissen, und ich werde mein Bestes tun, um sie zu beantworten.