Denken Sie daran, dass neue Objekte mit einer Konstruktorfunktion wie new F()
erstellt werden können.
Wenn F.prototype
ein Objekt ist, verwendet der new
Operator es, um [[Prototype]]
für das neue Objekt festzulegen.
Bitte beachten Sie:
JavaScript verfügte von Anfang an über eine prototypische Vererbung. Es war eines der Kernmerkmale der Sprache.
Aber früher gab es keinen direkten Zugang dorthin. Das Einzige, was zuverlässig funktionierte, war eine "prototype"
-Eigenschaft der Konstruktorfunktion, die in diesem Kapitel beschrieben wird. Es gibt also viele Skripte, die es immer noch verwenden.
Bitte beachten Sie, dass F.prototype
hier eine reguläre Eigenschaft namens "prototype"
auf F
bedeutet. Es klingt etwas ähnlich wie der Begriff „Prototyp“, aber hier meinen wir tatsächlich eine reguläre Immobilie mit diesem Namen.
Hier ist das Beispiel:
let animal = { isst: stimmt }; Funktion Kaninchen(Name) { this.name = Name; } Rabbit.prototype = Tier; let Rabbit = new Rabbit("White Rabbit"); // Rabbit.__proto__ == Tier alarm( Rabbit.eats ); // WAHR
Das Setzen von Rabbit.prototype = animal
besagt wörtlich Folgendes: „Wenn ein new Rabbit
erstellt wird, weisen Sie seinen [[Prototype]]
dem animal
zu.“
Das ist das resultierende Bild:
Auf dem Bild ist "prototype"
ein horizontaler Pfeil, der eine reguläre Eigenschaft bedeutet, und [[Prototype]]
ist vertikal, was die Vererbung von rabbit
von animal
bedeutet.
F.prototype
wurde nur zum new F
Zeitpunkt verwendet
F.prototype
Eigenschaft wird nur verwendet, wenn new F
aufgerufen wird, sie weist [[Prototype]]
des neuen Objekts zu.
Wenn sich nach der Erstellung F.prototype
Eigenschaft ändert ( F.prototype = <another object>
), haben neue Objekte, die durch new F
erstellt wurden, ein anderes Objekt als [[Prototype]]
, aber bereits vorhandene Objekte behalten das alte.
Jede Funktion verfügt über die Eigenschaft "prototype"
auch wenn wir sie nicht bereitstellen.
Der standardmäßige "prototype"
ist ein Objekt mit dem einzigen constructor
, der auf die Funktion selbst verweist.
So was:
Funktion Rabbit() {} /* Standardprototyp Rabbit.prototype = { Konstruktor: Rabbit }; */
Wir können es überprüfen:
Funktion Rabbit() {} // standardmäßig: // Rabbit.prototype = { Konstruktor: Rabbit } alarm( Rabbit.prototype.constructor == Rabbit ); // WAHR
Wenn wir nichts tun, steht die constructor
Eigenschaft natürlich allen Kaninchen über [[Prototype]]
zur Verfügung:
Funktion Rabbit() {} // standardmäßig: // Rabbit.prototype = { Konstruktor: Rabbit } let Rabbit = new Rabbit(); // erbt von {constructor: Rabbit} alarm(rabbit.constructor == Rabbit); // true (vom Prototyp)
Wir können die Eigenschaft constructor
verwenden, um ein neues Objekt mit demselben Konstruktor wie das vorhandene zu erstellen.
Wie hier:
Funktion Kaninchen(Name) { this.name = Name; alarm(name); } let Rabbit = new Rabbit("White Rabbit"); let Rabbit2 = new Rabbit.constructor("Black Rabbit");
Das ist praktisch, wenn wir ein Objekt haben, nicht wissen, welcher Konstruktor dafür verwendet wurde (z. B. stammt es aus einer Bibliothek eines Drittanbieters) und wir ein weiteres Objekt derselben Art erstellen müssen.
Aber das Wichtigste an "constructor"
ist wahrscheinlich, dass…
…JavaScript selbst stellt nicht den richtigen "constructor"
-Wert sicher.
Ja, es existiert im Standard- "prototype"
für Funktionen, aber das ist alles. Was später damit passiert, liegt ganz bei uns.
Insbesondere wenn wir den Standardprototyp als Ganzes ersetzen, gibt es keinen "constructor"
darin.
Zum Beispiel:
Funktion Rabbit() {} Rabbit.prototype = { Sprünge: wahr }; let Rabbit = new Rabbit(); alarm(rabbit.constructor === Rabbit); // FALSCH
Um also den richtigen "constructor"
beizubehalten, können wir Eigenschaften zum Standard- "prototype"
hinzufügen/entfernen, anstatt ihn als Ganzes zu überschreiben:
Funktion Rabbit() {} // Rabbit.prototype nicht vollständig überschreiben // einfach hinzufügen Rabbit.prototype.jumps = true // Der standardmäßige Rabbit.prototype.constructor bleibt erhalten
Oder erstellen Sie alternativ die constructor
manuell neu:
Rabbit.prototype = { Sprünge: wahr, Konstrukteur: Kaninchen }; // jetzt ist der Konstruktor auch korrekt, weil wir ihn hinzugefügt haben
In diesem Kapitel haben wir kurz beschrieben, wie ein [[Prototype]]
für Objekte festgelegt wird, die über eine Konstruktorfunktion erstellt wurden. Später werden wir fortgeschrittenere Programmiermuster sehen, die darauf basieren.
Alles ist ganz einfach, nur ein paar Anmerkungen zur Verdeutlichung:
Die F.prototype
Eigenschaft (nicht mit [[Prototype]]
verwechseln) legt [[Prototype]]
für neue Objekte fest, wenn new F()
aufgerufen wird.
Der Wert von F.prototype
sollte entweder ein Objekt oder null
sein: andere Werte funktionieren nicht.
Die Eigenschaft "prototype"
hat nur dann einen solchen besonderen Effekt, wenn sie für eine Konstruktorfunktion festgelegt und mit new
aufgerufen wird.
An regulären Objekten ist der prototype
nichts Besonderes:
let user = { Name: „John“, Prototyp: „Bla-bla“ // überhaupt keine Magie };
Standardmäßig haben alle Funktionen F.prototype = { constructor: F }
, sodass wir den Konstruktor eines Objekts erhalten können, indem wir auf seine "constructor"
-Eigenschaft zugreifen.
Wichtigkeit: 5
Im folgenden Code erstellen wir new Rabbit
und versuchen dann, seinen Prototyp zu ändern.
Am Anfang haben wir diesen Code:
Funktion Rabbit() {} Rabbit.prototype = { isst: stimmt }; let Rabbit = new Rabbit(); alarm( Rabbit.eats ); // WAHR
Wir haben eine weitere Zeichenfolge hinzugefügt (hervorgehoben). Was wird jetzt alert
?
Funktion Rabbit() {} Rabbit.prototype = { isst: stimmt }; let Rabbit = new Rabbit(); Rabbit.prototype = {}; alarm( Rabbit.eats ); // ?
…Und wenn der Code so ist (eine Zeile ersetzt)?
Funktion Rabbit() {} Rabbit.prototype = { isst: stimmt }; let Rabbit = new Rabbit(); Rabbit.prototype.eats = false; alarm( Rabbit.eats ); // ?
Und so (eine Zeile ersetzt)?
Funktion Rabbit() {} Rabbit.prototype = { isst: stimmt }; let Rabbit = new Rabbit(); Kaninchen.eats löschen; alarm( Rabbit.eats ); // ?
Die letzte Variante:
Funktion Rabbit() {} Rabbit.prototype = { isst: stimmt }; let Rabbit = new Rabbit(); löschen Sie Rabbit.prototype.eats; alarm( Rabbit.eats ); // ?
Antworten:
true
.
Die Zuweisung zu Rabbit.prototype
richtet [[Prototype]]
für neue Objekte ein, hat jedoch keine Auswirkungen auf die vorhandenen.
false
.
Objekte werden durch Referenz zugewiesen. Das Objekt von Rabbit.prototype
wird nicht dupliziert, es ist immer noch ein einzelnes Objekt, auf das sowohl von Rabbit.prototype
als auch vom [[Prototype]]
von rabbit
verwiesen wird.
Wenn wir also seinen Inhalt durch eine Referenz ändern, ist er durch die andere sichtbar.
true
.
Alle delete
werden direkt auf das Objekt angewendet. Hier versucht delete rabbit.eats
, die Eigenschaft eats
aus rabbit
zu entfernen, hat sie aber nicht. Der Vorgang hat also keine Auswirkungen.
undefined
.
Die Eigenschaft eats
wird aus dem Prototyp gelöscht, sie existiert nicht mehr.
Wichtigkeit: 5
Stellen Sie sich vor, wir haben ein beliebiges Objekt obj
, das von einer Konstruktorfunktion erstellt wurde – wir wissen nicht, welches, aber wir möchten damit ein neues Objekt erstellen.
Können wir das so machen?
let obj2 = new obj.constructor();
Geben Sie ein Beispiel für eine Konstruktorfunktion für obj
, die dafür sorgt, dass solcher Code richtig funktioniert. Und ein Beispiel, das dafür sorgt, dass es falsch funktioniert.
Wir können diesen Ansatz verwenden, wenn wir sicher sind, dass die Eigenschaft "constructor"
den richtigen Wert hat.
Wenn wir beispielsweise nicht auf den Standardwert "prototype"
eingehen, funktioniert dieser Code mit Sicherheit:
Funktion Benutzer(name) { this.name = Name; } let user = new User('John'); let user2 = new user.constructor('Pete'); alarm( user2.name ); // Pete (hat funktioniert!)
Es hat funktioniert, weil User.prototype.constructor == User
.
…Aber wenn jemand sozusagen User.prototype
überschreibt und vergisst, constructor
neu zu erstellen, um auf User
zu verweisen, dann würde es fehlschlagen.
Zum Beispiel:
Funktion Benutzer(name) { this.name = Name; } User.prototype = {}; // (*) let user = new User('John'); let user2 = new user.constructor('Pete'); alarm( user2.name ); // undefiniert
Warum ist user2.name
undefined
?
So funktioniert new user.constructor('Pete')
:
Zuerst wird nach constructor
in user
gesucht. Nichts.
Dann folgt die Prototypenkette. Der Prototyp von user
ist User.prototype
und hat auch keinen constructor
(weil wir „vergessen“ haben, ihn richtig einzustellen!).
Weiter oben in der Kette ist User.prototype
ein einfaches Objekt, dessen Prototyp der integrierte Object.prototype
ist.
Schließlich gibt es für den integrierten Object.prototype
einen integrierten Object.prototype.constructor == Object
. Es wird also verwendet.
Am Ende haben wir schließlich let user2 = new Object('Pete')
.
Wahrscheinlich ist das nicht das, was wir wollen. Wir möchten new User
erstellen, kein new Object
. Das ist das Ergebnis des fehlenden constructor
.
(Nur für den Fall, dass Sie neugierig sind: Der Aufruf new Object(...)
wandelt sein Argument in ein Objekt um. Das ist eine theoretische Sache, in der Praxis ruft niemand new Object
mit einem Wert auf, und im Allgemeinen verwenden wir kein new Object
überhaupt Objekte herzustellen).