Umfassende Analyse von Prototypen, Prototypobjekten und Prototypketten in js

Umfassende Analyse von Prototypen, Prototypobjekten und Prototypketten in js

Prototypen verstehen

Jede von uns erstellte Funktion verfügt über eine Prototypeigenschaft, die ein Zeiger auf ein Objekt ist, dessen Zweck darin besteht, Eigenschaften und Methoden zu enthalten, die von allen Instanzen eines bestimmten Typs gemeinsam genutzt werden können. Betrachten Sie das folgende Beispiel:

Funktion Person(){
}
Person.Prototyp.Name = "ccc"
Person.Prototyp.Alter = 18
Person.prototype.sayName = Funktion (){
 konsole.log(dieser.name);
}

var person1 = neue Person()
person1.sayName() // --> ccc

var person2 = neue Person()
person2.sayName() // --> ccc

console.log(person1.sayName === person2.sayName) // --> true

Prototypobjekte verstehen

Gemäß dem obigen Code sehen Sie die folgende Abbildung:

Drei Punkte müssen verstanden werden:

  1. Wenn wir eine neue Funktion erstellen, wird für die Funktion gemäß einem bestimmten Regelsatz eine Prototypeigenschaft erstellt, die auf das Prototypobjekt der Funktion verweist. Das heißt, Person (Konstruktor) hat einen Prototypzeiger, der auf Person.prototype zeigt.
  2. Standardmäßig wird für jedes Prototypobjekt eine Konstruktoreigenschaft erstellt. Diese Eigenschaft ist ein Zeiger auf die Funktion, in der sich die Prototypeigenschaft befindet.
  3. Jede Instanz verfügt über einen Zeiger (interne Eigenschaft), der auf das Prototypobjekt des Konstruktors verweist. Das heißt, person1 und person2 haben beide eine interne Eigenschaft __proto__ (in ECMAscript wird dieser Zeiger [[prototype]] genannt, obwohl es keine Standardmethode gibt, um in Skripten auf [[prototype]] zuzugreifen; Firefox, IE und Chrome unterstützen alle eine Eigenschaft namens __proto__), die auf Person.prototype verweist.

Hinweis: Es besteht keine direkte Beziehung zwischen den Instanzen person1 und person2 und dem Konstruktor.

Wie bereits erwähnt, ist [[Prototyp]] nicht in allen Implementierungen zugänglich. Woher wissen wir also, ob eine Beziehung zwischen der Instanz und dem Prototypobjekt besteht? Zur Beurteilung gibt es zwei Möglichkeiten:

  • Prototyp-Onlinemethode: isPrototypeOf(), wie zum Beispiel: console.log(Person.prototype.isPrototypeOf(person1)) // --> true
  • Eine neue Methode, die in ECMAscript5 hinzugefügt wurde: Object.getPrototypeOf(), diese Methode gibt den Wert von [[prototype]] zurück. Beispiel: console.log (Object.getPrototypeOf(person1) === Person.prototype) // --> true

Beziehung zwischen Instanzeigenschaften und Prototypeigenschaften

Wir haben bereits erwähnt, dass der Prototyp zunächst nur die Konstruktoreigenschaft enthält, die ebenfalls gemeinsam genutzt wird und daher über die Objektinstanz zugänglich ist. Obwohl Sie über eine Objektinstanz auf die im Prototyp gespeicherten Werte zugreifen können, können Sie die Werte im Prototyp nicht über eine Objektinstanz überschreiben. Wenn wir einer Instanz eine Eigenschaft hinzufügen und die Eigenschaft denselben Namen wie eine Eigenschaft im Instanzprototyp hat, wird die Eigenschaft auf der Instanz erstellt und die Eigenschaft im Prototyp wird maskiert. wie folgt:

Funktion Person() {}
Person.Prototyp.Name = "ccc";
Person.Prototyp.Alter = 18;
Person.prototype.sayName = Funktion() {
 konsole.log(dieser.name);
};

var person1 = neue Person();
var person2 = neue Person();

person1.name = 'www' // Füge ein Namensattribut zu person1 hinzu person1.sayName() // --> 'www'————'Aus der Instanz'
person2.sayName() // --> 'ccc'————'vom Prototyp'

console.log(person1.hasOwnProperty('name')) // --> true
console.log(person2.hasOwnProperty('name')) // --> false

delete person1.name // --> Lösche das neu hinzugefügte Namensattribut in person1 person1.sayName() // -->'ccc'————'from prototype'

Wie bestimmen wir, ob eine Eigenschaft eine Instanzeigenschaft oder eine Prototypeneigenschaft ist? Hier kann mit der Methode hasOwnProperty() ermittelt werden, ob eine Eigenschaft in der Instanz oder im Prototyp vorhanden ist. (Diese Methode wird von Object geerbt)

Die folgende Abbildung analysiert detailliert die Beziehung zwischen der Implementierung und dem Prototyp des obigen Beispiels in verschiedenen Situationen: (die Beziehung des Personenkonstruktors wird weggelassen)

Einfachere Prototyp-Syntax

Wir können nicht jedes Mal, wenn wir eine Eigenschaft und Methode hinzufügen, „Person.prototype“ eingeben, wie im vorherigen Beispiel. Um unnötige Tipparbeit zu vermeiden, ist folgender Ansatz üblicher:

Funktion Person(){}
Person.Prototyp = {
 Name: "ccc",
 Alter: 18,
 sageName: Funktion () {
 console.log(dieser.Name)
 }
}

Im obigen Code setzen wir Person.prototype gleich einem neuen Objekt, das als Objektliteral erstellt wurde. Das Endergebnis ist dasselbe, mit einer Ausnahme: Die Konstruktoreigenschaft verweist nicht mehr auf Person. Wie bereits erwähnt, wird bei jeder Erstellung einer Funktion gleichzeitig ihr Prototypobjekt erstellt und dieses Objekt erhält automatisch die Konstruktor-Eigenschaft. Aber in der neuen Syntax, die wir verwenden, wird das Standard-Prototypobjekt im Wesentlichen vollständig neu geschrieben, sodass die Konstruktoreigenschaft zur Konstruktoreigenschaft des neuen Objekts wird (und auf den Objektkonstruktor zeigt) und nicht mehr auf die Person-Funktion zeigt. An diesem Punkt kann der Operator „instanceof“ zwar noch immer das richtige Ergebnis zurückgeben, der Typ des Objekts lässt sich jedoch nicht mehr über den Konstruktor ermitteln. wie folgt:

var person1 = neue Person()
console.log(person1 Instanz von Objekt) // --> true
console.log(person1 Instanz von Person) // --> true
konsole.log(person1.constructor === Person) // --> false
konsole.log(person1.constructor === Objekt) // --> true

Hier wird der Operator „instanceof“ verwendet, um „Object“ und „Person“ zu testen und gibt trotzdem „true“ zurück. Die Konstruktor-Eigenschaft ist gleich „Object“, nicht „Person“. Wenn der Konstruktor wirklich wichtig ist, können Sie ihn wie folgt schreiben:

Funktion Person(){}
Person.Prototyp = {
 Konstruktor: Person, // --> Name zurücksetzen: 'ccc',
 Alter: 18,
 sageName: Funktion () {
 console.log(dieser.Name)
 }
}

Dies führt jedoch zu einem neuen Problem. Das Zurücksetzen der Konstruktoreigenschaft auf die oben beschriebene Weise führt dazu, dass ihr Attribut [[Enumerable]] auf true gesetzt wird. Standardmäßig sind native Konstruktoreigenschaften nicht aufzählbar. Wenn Sie also eine ECMAscript5-kompatible JavaScript-Engine verwenden möchten, können Sie Object.defineProperty() ausprobieren.

Funktion Person(){}
Person.Konstruktor = {
 Name: "ccc", 
 Alter: 18,
 sageName: function(){
 console.log(dieser.Name)
 }
}
// Setzt den Konstruktor zurück, gilt nur für ECMAscript5-kompatible Browser Object.defineProperty(Person.constructor, "constructor", {
 aufzählbar: falsch, 
 Wert: Person
})

Dynamik des Prototyps

Da es sich bei der Suche nach einem Wert im Prototyp um eine einzelne Suche handelt, werden alle Änderungen, die wir am Prototypobjekt vornehmen, sofort in der Instanz widergespiegelt. Zum Beispiel:

Funktion Person(){}
var person1 = neue Person()
Person.prototype.sayHi = Funktion(){
 console.log('hallo')
}
person1.sayHi()

Im obigen Code erstellen wir zuerst eine Person-Instanz und speichern sie in person1. Dann fügen wir die Methode sayHi() zu Person.prototype hinzu. Obwohl person1 vor dem Hinzufügen der neuen Methode erstellt wurde, kann sie weiterhin auf diese Methode zugreifen. Der Grund liegt in der losen Verbindung zwischen Instanzen und Prototypen.
Obwohl Sie einem Prototyp jederzeit Eigenschaften und Methoden hinzufügen können, werden diese sofort in der Instanz widergespiegelt. Wenn Sie jedoch das gesamte Prototypobjekt neu schreiben, ist die Situation anders. Sehen Sie sich den folgenden Code an:

Funktion Person(){}
var person1 = neue Person()

Person.Prototyp = {
 Name: "ccc",
 Alter: 18,
 sageName: function(){
 console.log(dieser.Name)
 }
}

person1.sayName() // --> Fehler

Zur Analyse siehe folgende Abbildung:

Wenn der Konstruktor aufgerufen wird, wird der Instanz ein [[Prototyp]]-Zeiger auf den ursprünglichen Prototyp hinzugefügt, und das Ändern des Prototyps in ein anderes Objekt ist gleichbedeutend mit dem Trennen der Verbindung zwischen dem Konstruktor und dem ursprünglichen Prototyp. Denken Sie daran: Der Zeiger in der Instanz zeigt nur auf den Prototyp, nicht auf den Konstruktor.

Die Prototypenkette verstehen

Die Prototypenkette ist die Hauptmethode zur Erreichung der Vererbung. Die Grundidee besteht darin, dass ein Referenztyp die Eigenschaften und Methoden eines anderen Referenztyps erbt. Bevor wir die Prototypenkette verstehen, müssen wir zunächst die Beziehung zwischen Prototyp, Prototypobjekt und Instanz klären: Jeder Konstruktor hat ein Prototypobjekt, das Prototypobjekt enthält einen Zeiger auf den Konstruktor und die Instanz enthält einen internen Zeiger auf das Prototypobjekt. Was wäre, wenn wir das Prototypobjekt einer Instanz eines anderen Typs gleichsetzen würden? Offensichtlich enthält dieses Prototypobjekt einen Zeiger auf einen anderen Prototyp. Schauen Sie sich zuerst den Code und dann das Bild an:

Funktion SuperType(){
 diese.Eigenschaft = true
}

SuperType.prototype.getSuperValue = Funktion(){
 gib diese Eigenschaft zurück
}

Funktion SubType(){
 diese.subEigenschaft = false
}

// Erbt SuperType
SubType.prototype = neuer SuperType()

SubType.prototype.getSubValue = Funktion (){
 gib diese Untereigenschaft zurück
}

var Instanz = neuer Subtyp()
console.log(Instanz.getSuperValue()) // --> true

Der obige Code definiert zwei Typen: SuperType und SubType. Jeder Typ hat eine Eigenschaft und eine Methode.

Analysieren Sie die obige Abbildung: Die Instanz verweist auf den SubType-Prototyp und der SubType-Prototyp verweist auf den SuperType-Prototyp. Die Methode getSuperValue() befindet sich noch in SuperType.prototype, aber die Eigenschaft befindet sich in SubType.prototype. Dies liegt daran, dass die Eigenschaft ein Instanzattribut ist, während getSuperValue() eine Prototypmethode ist. Da SubType.prototype jetzt eine Instanz von SuperType ist, befindet sich die Eigenschaft natürlich in dieser Instanz. Beachten Sie auch, dass instance.constructor jetzt auf SuperType verweist, da der ursprüngliche Konstruktor in SubType.prototype neu geschrieben wurde.
Warum gibt es „true“ zurück?
Analyse: Beim Aufrufen der Methode instance.getSuperValue() werden drei Suchschritte durchlaufen:

Suche nach Instanzen Suche nach SubType.prototype
Durchsuchen Sie SuperType.prototype, bis Sie die Methode hier finden. Wenn keine Eigenschaft oder Methode gefunden wird, wird der Suchvorgang immer Glied für Glied bis zum Ende der Prototypenkette fortgesetzt, bevor er angehalten wird.

Vergessen Sie nicht den Standardprototyp

Sie sollten wissen, dass alle Referenztypen standardmäßig Objekte erben und diese Vererbung auch über die Prototypenkette implementiert wird. Der Standardprototyp aller Funktionen ist eine Instanz von Object. Daher enthält der Standardprototyp einen internen Zeiger, der auf Object.prototype zeigt. Deshalb haben alle benutzerdefinierten Typen die Methoden toString() und valueOf(). Die komplette Prototypenkette sollte also wie folgt aussehen:
Schauen Sie sich die folgende Abbildung an, das Innere des Untertyps:

Detailliertes Diagramm:

Kurz gesagt: SubType erbt SuperType und SuperType erbt Object. Beim Aufruf von instant.toString() wird tatsächlich die in Object.prototype gespeicherte Methode aufgerufen.

Bestimmen Sie die Beziehung zwischen Prototypen und Instanzen

Wenn eine Prototypkette sehr lang ist, gibt es zwei Möglichkeiten, die Beziehung zwischen dem Prototyp und der Instanz zu bestimmen:

Verwenden Sie den Operator „instanceof“. Solange Sie diesen Operator zum Testen der Instanz und des Konstruktors verwenden, der in der Prototypkette erscheint, gibt das Ergebnis „true“ zurück.

console.log(Instanz Instanz von Objekt) // --> true
console.log(Instanz Instanz von SuperType) // --> true
console.log(Instanz Instanz von SubType) // --> true

Verwenden Sie die Methode isPrototypeOf (), die der Beurteilungsmethode instantof ähnelt. Solange der Prototyp in der Prototypkette erscheint, wird true zurückgegeben.

console.log(Objekt.prototype.isPrototypeOf(Instanz)) // --> true
console.log(SuperType.prototype.isPrototypeOf(Instanz)) // --> true
console.log(SubType.prototype.isPrototypeOf(Instanz)) // --> true

Definieren Sie Methoden sorgfältig

Manchmal muss ein Subtyp eine Methode in einem Supertyp überschreiben oder eine Methode hinzufügen, die im Supertyp nicht vorhanden ist. Der Code zum Hinzufügen von Methoden zum Prototyp muss jedoch in jedem Fall nach der Anweisung platziert werden, die den Prototyp ersetzt. wie folgt:

Funktion SuperType(){
 diese.Eigenschaft = wahr;
}
SuperType.prototype.getSuperValue = Funktion(){
 gib diese Eigenschaft zurück
}

Funktion SubType(){
 diese.Untereigenschaft = falsch;
}

// Erbt SuperType
SubType.prototype = neuer SuperType()

// Neue Methode hinzufügen SubType.prototype.getSubValue = function(){
 gib diese Untereigenschaft zurück
}
// Überschreibe die Methode im Supertyp SubType.prototype.getSuperValue = function(){
 return false
}

var Instanz = neuer Subtyp()
console.log(Instanz.getSuperValue()) // --> false
var InstanzSuper = neuer SuperType()
console.log(instanceSuper.getSuperValue()) // -> true

Im obigen Code wird die erste Methode getSubValue() zu SubType hinzugefügt. Die zweite Methode, getSuperValue(), ist eine Methode, die bereits in der Prototypkette vorhanden ist. Durch das Überschreiben dieser Methode wird jedoch die ursprüngliche Methode maskiert. Das heißt, wenn getSuperValue() über eine Instanz von SubType aufgerufen wird, wird die neu definierte Methode aufgerufen, aber wenn getSuperValue() über eine Instanz von SuperType aufgerufen wird, wird weiterhin die ursprüngliche Methode aufgerufen. Ein weiterer Punkt ist, dass Sie bei der Implementierung der Vererbung über die Prototypkette keine Objektvariablen zum Erstellen von Prototypmethoden verwenden können, da dies die Prototypkette neu schreiben und dazu führen würde, dass die Prototypkette unterbrochen wird.

Das Problem mit der Prototypenkette

Wenn die Vererbung über Prototypen implementiert wird, wird der Prototyp tatsächlich zu einer Instanz eines anderen Typs, sodass die ursprünglichen Instanzeigenschaften zu den aktuellen Prototypeigenschaften werden, was dazu führt, dass die Eigenschaften gemeinsam genutzt werden. Sehen Sie sich den folgenden Code an:

Funktion SuperType(){
 this.colors = ['weiß', 'blau']
}

Funktion SubType(){
}

// Erbt SuperType
SubType.prototype = neuer SuperType()
var Instanz1 = neuer Subtyp()
Instanz1.Farben.Push('rot')

var Instanz2 = neuer Subtyp()
console.log(instance1.colors) // -->["weiß", "blau", "rot"]
console.log(instance2.colors) // -->["weiß", "blau", "rot"]

Beim Erstellen einer Instanz eines Subtyps können Sie keine Parameter an den Konstruktor des Supertyps übergeben. Tatsächlich sollte es keine Möglichkeit geben, Parameter an den Supertyp-Konstruktor zu übergeben, ohne alle Objektinstanzen zu beeinflussen. Daher wird die Prototypenkette in der Praxis selten allein verwendet.

Oben finden Sie den detaillierten Inhalt des Diagramms von Prototyp, Prototypobjekt und Prototypkette in js. Weitere Informationen zu Prototyp, Prototypobjekt und Prototypkette in js finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Detaillierte Erklärung von Prototypen und Prototypenketten in JavaScript
  • Detaillierte Erklärung der JavaScript-Prototypenkette
  • Details zum JavaScript-Prototyp und zur Prototypkette
  • Verstehen Sie JavaScript-Prototypen und Prototypenketten gründlich
  • Tiefgreifendes Verständnis von Javascript-Prototypen und Prototypenketten

<<:  So ändern Sie Port 3389 des Remotedesktops von Windows Server 2008 R2

>>:  Lösung für den Fehler von MySQL, innobackupex zum Sichern des Verbindungsservers zu verwenden

Artikel empfehlen

Häufige Browserkompatibilitätsprobleme (Zusammenfassung)

Browserkompatibilität ist nichts anderes als Stil...

W3C Tutorial (6): W3C CSS Aktivitäten

Ein Stylesheet beschreibt, wie ein Dokument angez...

Tutorial zur Installation von MYSQL8.0 auf Alibaba Cloud ESC

Öffnen Sie das Verbindungstool. Ich verwende Moba...

Erfahren Sie mehr über JavaScript-Iteratoren

Inhaltsverzeichnis Einführung Wie sieht ein Itera...

Detaillierte Erklärung des Befehlsmodus in der Javascript-Praxis

Inhaltsverzeichnis Definition Struktur Beispiele ...

CSS löst das Fehlausrichtungsproblem von Inline-Blöcken

Schluss mit Unsinn, Postleitzahl HTML-Teil <di...

Detaillierte Erläuterung gängiger Methoden von JavaScript Array

Inhaltsverzeichnis Methoden, die das ursprünglich...

Teilen Sie einige wichtige Interviewfragen zum MySQL-Index

Vorwort Ein Index ist eine Datenstruktur, die ein...

JavaScript zur Implementierung der Webversion des Gobang-Spiels

In diesem Artikel wird der spezifische Code für J...

Einige Schlussfolgerungen zur Entwicklung mobiler Websites

Die mobile Version der Website sollte zumindest ü...

Centos8 erstellt NFS basierend auf KDC-Verschlüsselung

Inhaltsverzeichnis Konfiguration NFS-Server (nfs....