1. Funktionserstellungsprozess
Bevor wir die Prototypenkette verstehen, schauen wir uns zunächst an, was eine Funktion während des Erstellungsprozesses tut:
1 Funktion A() {};
Wenn wir eine solche leere Funktion im Code deklarieren, ist die Essenz der js-Analyse (das oberflächliche Verständnis muss vertieft werden):
1 Objekt (mit Konstruktorattribut und [[Prototyp]]-Attributen), laut ECMA ist das [[Prototyp]]-Attribut unsichtbar und nicht aufzählbar
2. Erstellen Sie eine Funktion (mit Namen und Prototypattributen), und referenzieren Sie es dann über das Prototyp-Attribut. Erstelltes Objekt
3. Erstellen Sie Variable A und weisen Sie der Variablen A die Funktionsreferenz
zu, wie unten gezeigt:
(Beachten Sie, dass die Zahlen in der Abbildung alle „Referenz“-Typen sind)
Die Erstellung jeder Funktion durchläuft den oben genannten Prozess.
2. Konstruktor
Was ist also ein Konstruktor?
Gemäß der Definition von ECMA
Konstruktor ist eine Funktion, die das neu erstellte Objekt erstellt und initialisiert.
Konstruktor ist eine Funktion, die zum Erstellen und Initialisieren eines neuen Objekts verwendet wird.
Mit welcher Funktion können gleichzeitig neue Objekte erstellt und initialisiert werden? Die Antwort lautet: jede Funktion, auch leere Funktionen.
Die Schlussfolgerung lautet also: Jede Funktion kann ein Konstruktor sein.
3. Prototyp
Gemäß dem vorherigen leeren Funktionserstellungsdiagramm wissen wir, dass jede Funktion beim Erstellen automatisch das Prototypattribut hinzufügt Sehen Sie, dass sein Wesen ein Verweis auf ein Objekt ist (dieses Objekt wird vorübergehend als Prototypobjekt bezeichnet).
Wir können das Prototypobjekt der Funktion wie ein gewöhnliches Objekt bearbeiten! Lassen Sie es uns gemeinsam bestätigen.
Fügen Sie etwas Code um die leere Funktion hinzu, die Sie gerade erstellt haben:
function A() {
this.width = 10;
this.data = [1,2,3] ;
this .key = "this is A";
}
A._objectNum = 0;//Definieren Sie die Attribute von A
A.prototype.say = function(){//Give A Fügen Sie dem Attribute hinzu Prototypobjekt
Alert("Hallo Welt")
}
Die Zeilen 7 bis 9 des Codes fügen dem Prototypobjekt der Funktion ein Say-Attribut hinzu und beziehen sich auf eine anonyme Funktion gemäß der „Funktionserstellung“. "Prozess, das Diagramm sieht wie folgt aus:
(Der graue Hintergrund ist das Attribut, das basierend auf der leeren Funktion hinzugefügt wurde)
Einfach ausgedrückt ist der Prototyp ein Attribut von Die Funktion wird während des Erstellungsprozesses automatisch vom js-Compiler hinzugefügt.
Was nützen Prototypen?
Verstehen Sie zunächst den neuen Operator wie folgt:
1
2 var a1 = new A;
var a2 = new A;
So erstellen Sie dann ein Objekt über einen Konstruktor Warum sollten wir Objekte wie dieses erstellen, anstatt direkt var a1 = {};? Dies umfasst die spezifischen Schritte von new. Die neue Operation kann hier in drei Schritte unterteilt werden (am Beispiel der Erstellung von a1):
1 Erstellen Sie ein neues Objekt und weisen Sie es der Variablen a1 zu: var a1 = {} ;
2 , richten Sie das [[Prototype]]-Attribut dieses Objekts auf das Prototypobjekt der Funktion A: a1.[[Prototype]] = A.prototype
3 Zeigen Sie dies auf das in 1 erstellte Objekt a1, um das Objekt zu initialisieren: A.apply(a1,arguments)
Das Strukturdiagramm lautet wie folgt:
Wie Sie auf dem Bild sehen können, gibt es unabhängig davon, ob es sich um Objekt a1 oder a2 handelt, ein Attribut, das einen Verweis auf das Prototypobjekt der Funktion A speichert. Für diese Objekte finden sich einige gängige Methoden im Prototyp von Funktion, spart Speicherplatz.
4. Prototypkette
Nachdem wir den neuen Operator und die Rolle des Prototyps verstanden haben, werfen wir einen Blick darauf, was [[Prototyp]] ist. Und wie sucht das Objekt entlang dieser Referenz nach Attributen?
In der Welt von js verfügt jedes Objekt standardmäßig über ein [[Prototyp]]-Attribut, und die darin gespeicherte Adresse stellt die Prototypenkette des Objekts dar. Sie wird beim Erstellen des Objekts automatisch hinzugefügt. Sein Wert wird durch den rechten Parameter des neuen Operators bestimmt: Wenn wir var object1 = {};, zeigt der [[Prototyp]] von object1 auf das Prototypobjekt des Objektkonstruktors, da var object1 = {}; ist gleich var object = new Object(); (den Grund finden Sie im obigen Analyseprozess von new A).
Wenn ein Objekt nach einem Attribut sucht, durchläuft es zunächst seine eigenen Attribute. Wenn keine vorhanden sind, wird die Suche nach dem von [[Prototyp]] referenzierten Objekt fortgesetzt um nach dem auf [[Prototype]].[[Prototype] verwiesenen Objekt zu suchen usw., bis [[Prototype]]...[[Prototype]] undefiniert ist ([[Prototype]] von Object ist undefiniert)
Wie im Bild oben gezeigt:
//Wir möchten a1.fGetName erhalten
Alert(a1.fGetName);//Ausgabe undefiniert
//1. Durchlaufen Sie das a1-Objekt selbst www.2cto .com
//Das Ergebnis ist, dass das a1-Objekt selbst kein fGetName-Attribut hat
//2. Finden Sie den [[Prototyp]] von a1, der das entsprechende Objekt A.prototype ist, und durchlaufen Sie ihn gleichzeitig Zeit
//Das Ergebnis ist, dass A.prototype dieses Attribut auch nicht hat
//3. A.prototype finden Der [[Prototype]] des Objekts zeigt auf das entsprechende Objekt Object.prototype
//Das Ergebnis ist, dass Object.prototype nicht über fGetName
verfügt //4 Versuchen Sie, das Attribut [[Prototype]] von Object.prototype zu finden, und das Ergebnis wird undefiniert zurückgegeben, dies ist der Wert von a1.fGetName
Einfach ausgedrückt speichert es einen Verweis auf ein anderes Objekt über den [[Prototyp]] des Objekts und sucht über diesen Verweis nach Attributen. Dies ist die Prototypenkette.
5. Vererbung
Mit dem Konzept der Prototypenkette kann eine Vererbung durchgeführt werden.
1 Funktion B() {};
Zu diesem Zeitpunkt wird der Prototyp B.prototype von B generiert
Der Prototyp selbst ist ein Objektobjekt. Wir können sehen, welche Daten darin platziert sind.
B.prototype Actual Das Obige ist {constructor: B, [[Prototype]]: Object.prototype🎜>Da der Prototyp selbst eine Instanz eines Object-Objekts ist, zeigt seine Prototypenkette auf den Prototyp von Object
B. prototyp = A.prototype ;//Es ist gleichbedeutend damit, den Prototyp von B auf den Prototyp von A zu verweisen; dies erbt nur die Prototyp-Methode von A, und die benutzerdefinierte Methode in A erbt nicht
B.prototype.thisisb = "das ist Konstruktor B" ;/ /Dies wird auch den Prototyp von a ändern
Aber wir wollen nur die Prototypenkette von B auf A verweisen. Wie erreicht man das?
Die erste besteht darin, die Referenzadresse der Prototypkette zu ändern
1 B.prototype.__proto__ = A.prototype;
Es gibt keine __proto__-Methode in ECMA. Diese wird von js-Interpretern wie ff und chrome hinzugefügt. , was dem [[Prototyp]] von EMCA entspricht. Dies ist keine Standardmethode. Wie verwendet man die Standardmethode?
Wir wissen, dass wir beim Betrieb von new tatsächlich nur die Prototypkette des Instanzobjekts auf den Prototypadressblock des Konstruktors verweisen, dann können wir wie folgt vorgehen:
1 B.prototype = new A();
Auf diese Weise Das Ergebnis ist:
generiert eine Instanz von A und weist sie dem Prototyp von B zu, d. h. B.prototype entspricht dem Objekt {width: 10, data: [1,2,3] , Schlüssel: "das ist A " , [[Prototype]] : A.prototype🎜>Auf diese Weise wird der Prototyp von A über das Objektattribut B.prototype.[[Prototype]] gespeichert und bildet einen Link zu der Prototyp
Beachten Sie jedoch, dass sich auf diese Weise B Der Konstruktor des generierten Objekts geändert hat, da in B kein Konstruktorattribut vorhanden ist und A.prototype nur aus der Prototypenkette gefunden werden kann und der Konstruktor gelesen wird out: A
var b = new B;
console.log(b .constructor);//output A
Also müssen wir B selbst manuell zurücksetzen
B.prototype.constructor = B ;
//Jetzt wird der Prototyp von B zu {Breite: 10, Daten: [1,2,3], Schlüssel: „Das ist A“, [[Prototyp]]: A.Prototyp, Konstruktor: B 🎜> console.log(b.constructor);//Ausgabe B
//Gleichzeitig erbt B die benutzerdefinierten Attribute Breite und Name von A direkt über den Prototyp
console.log(b.data);/ /output [1,2,3]
//Der Nachteil davon ist
b.data.push(4);//Das Datenarray des Prototyps (Referenz) direkt geändert
var c = new B ;
warning(c.data);//output [1,2,3 ,4]
//Eigentlich wollen wir nur die Prototypenkette. Wir wollen die benutzerdefinierten Eigenschaften von A in B definieren (nicht im Prototyp)
//Wie erbe ich?
//Da wir die benutzerdefinierten Attribute nicht in A haben möchten, können wir eine Möglichkeit finden, sie herauszufiltern
//Wir können eine neue leere Funktion erstellen
Funktion F(){}
//Legen Sie den leeren Prototyp der Funktion auf den Prototyp des Konstruktors A
F.prototype = A.prototype;
//Verwenden Sie zu diesem Zeitpunkt die neue Operation, um auf die Prototypenkette von B.prototype zu zeigen zum Prototyp von F
B.prototype = neues F;
//Zu diesem Zeitpunkt wird der Prototyp von B zu {[[Prototype]]: F.prototype}
//Hier ist F.prototype eigentlich nur ein Verweis auf eine Adresse
//Aber der Konstruktor der Instanz erstellt von B zeigt auf A, daher müssen wir hier das Konstruktorattribut von B.prototype anzeigen und festlegen
B.prototype.constructor = B;
// Zu diesem Zeitpunkt wird der Prototyp von B zu {constructor: B, [[Prototype]]: F.prototype}
//Dies implementiert die Prototypenvererbung von B von A.
Das Diagramm sieht wie folgt aus, wobei der rote Teil die Prototypenkette darstellt: