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.
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
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 meinObjekt
Methode, 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!
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.
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 (); ;
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.
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 String
Prototyp. 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.
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.