Im wahrsten Sinne des Wortes ist Javascript keine objektorientierte Sprache und bietet keine traditionelle Vererbungsmethode. Es bietet jedoch eine Prototyp-Vererbungsmethode, bei der die von ihm selbst bereitgestellten Prototyp-Eigenschaften verwendet werden, um die Vererbung zu erreichen.
Prototypen und Prototypenketten
Bevor wir über die prototypische Vererbung sprechen, müssen wir noch über Prototypen und Prototypenketten sprechen. Dies ist schließlich die Grundlage für die Realisierung der prototypischen Vererbung.
In Javascript verfügt jede Funktion über ein Prototypattribut, das auf ihren eigenen Prototyp verweist, und das von dieser Funktion erstellte Objekt verfügt auch über ein __proto__-Attribut, das auf diesen Prototyp zeigt, und der Prototyp der Funktion ist ein Objekt, sodass dieses Objekt auch ein Objekt hat __proto__ zeigt auf seinen eigenen Prototyp und geht Schicht für Schicht tiefer, bis es den Prototyp des Objektobjekts erreicht, wodurch eine Prototypenkette gebildet wird. Das Bild unten erklärt die Beziehung zwischen Prototyp und Prototypkette in Javascript sehr gut.
Jede Funktion ist ein von der Funktion Funktion erstelltes Objekt, daher verfügt jede Funktion auch über ein __proto__-Attribut, das auf den Prototyp der Funktion Funktion verweist. Hier muss darauf hingewiesen werden, dass es das __proto__-Attribut jedes Objekts ist, das tatsächlich die Prototypenkette bildet, und nicht das Prototypattribut der Funktion, was sehr wichtig ist.
Prototypische Vererbung
Basismodus
var Child = function(){
This.name = 'child' ;
} ;
Child.prototype = new Parent() ;
var parent = new Parent() ;
var child = new Child() ;
console.log(parent.getName()) ; //parent
console.log(child.getName()); //child
Dies ist die einfachste Möglichkeit, die Prototypenvererbung zu implementieren. Weisen Sie das Objekt der übergeordneten Klasse direkt dem Prototyp des Konstruktors der Unterklasse zu, sodass das Objekt der Unterklasse auf die Attribute im Prototyp der übergeordneten Klasse zugreifen kann der Konstruktor der übergeordneten Klasse. Das Prototyp-Vererbungsdiagramm dieser Methode lautet wie folgt:
Die Vorteile dieser Methode liegen auf der Hand. Die Implementierung ist sehr einfach und erfordert keine besonderen Vorgänge. Gleichzeitig liegen die Nachteile auf der Hand, wenn die Unterklasse die gleiche Initialisierungsaktion durchführen muss Konstruktor, dann muss es in der Unterklasse durchgeführt werden. Wiederholen Sie im Konstruktor den Vorgang in der übergeordneten Klasse:
var Child = function(name){
This.name = Name ||. 'Kind' ;
} ;
Child.prototype = new Parent() ;
var parent = new Parent('myParent') ;
var child = new Child('myChild') ;
console.log(parent.getName()) ; //myParent
console.log(child.getName()); //myChild
Im obigen Fall muss nur das Namensattribut initialisiert werden. Wenn der Initialisierungsaufwand weiter zunimmt, ist diese Methode sehr unpraktisch. Daher gibt es die folgende verbesserte Methode.
Konstruktor ausleihen
var Child = function(name){
Parent.apply(this,arguments) ;
} ;
Child.prototype = new Parent() ;
var parent = new Parent('myParent') ;
var child = new Child('myChild') ;
console.log(parent.getName()) ; //myParent
console.log(child.getName()); //myChild
Die obige Methode führt die gleiche Initialisierungsarbeit durch, indem sie den Konstruktor der übergeordneten Klasse über „Apply“ im Unterklassenkonstruktor aufruft. Auf diese Weise kann die Unterklasse unabhängig davon, wie viel Initialisierungsarbeit in der übergeordneten Klasse ausgeführt wird, ebenfalls ausgeführt werden Initialisierungsarbeiten. Bei der obigen Implementierung gibt es jedoch immer noch ein Problem, einmal im Unterklassenkonstruktor und einmal beim Zuweisen des Unterklassenprototyps. Daher müssen wir noch eine Verbesserung vornehmen
Parent.apply(this,arguments) ;
} ;
Child.prototype = Parent.prototype ;
var child = new Child('myChild') ;
console.log(child.getName()); //myChild
Temporäres Konstruktormuster (Holy Grail Pattern)
Die letzte verbesserte Version, die das obige Konstruktormuster übernimmt, weist immer noch Probleme auf. Sie weist den Prototyp der Unterklasse direkt zu. Dies führt zu einem Problem, wenn der Prototyp der Unterklasse geändert wird , dann wirken sich diese Änderungen auch auf den Prototyp der übergeordneten Klasse und damit auf das übergeordnete Klassenobjekt aus. Dies ist definitiv nicht das, was jeder sehen möchte. Um dieses Problem zu lösen, gibt es das temporäre Konstruktormuster.
Parent.apply(this,arguments) ;
} ;
var F = neue Funktion(){};
F.prototype = Parent.prototype ;
Child.prototype = new F() ;
var child = new Child('myChild') ;
console.log(child.getName()); //myChild
Es ist leicht zu erkennen, dass durch das Hinzufügen eines temporären Konstruktors F zwischen dem Prototyp der übergeordneten Klasse und dem Prototyp der untergeordneten Klasse die Verbindung zwischen dem Prototyp der untergeordneten Klasse und dem Prototyp der übergeordneten Klasse unterbrochen wird, sodass der Prototyp der untergeordneten Klasse geändert wird hat keinen Einfluss auf den Prototyp der übergeordneten Klasse.
Meine Methode
Der Holy Grail-Modus ist im „Javascript-Modus“ beendet, aber unabhängig von der oben genannten Methode liegt ein Problem vor, das nicht leicht zu finden ist. Sie können sehen, dass ich dem Prototypattribut von „Parent“ ein obj-Objektliteralattribut hinzugefügt habe, das jedoch nie verwendet wurde. Schauen wir uns die folgende Situation basierend auf dem Holy Grail-Modell an:
Parent.apply(this,arguments) ;
} ;
var F = neue Funktion(){};
F.prototype = Parent.prototype ;
Child.prototype = new F() ;
var child = new Child('myChild') ;
console.log(parent.obj.a); //1
child.obj.a = 2;
console.log(child.obj.a); //2
console.log(parent.obj.a); //2
Wenn ich in der obigen Situation das untergeordnete Objekt obj.a ändere, wird auch obj.a im Prototyp der übergeordneten Klasse geändert, was das gleiche Problem wie der gemeinsam genutzte Prototyp verursacht. Diese Situation tritt auf, weil wir beim Zugriff auf child.obj.a der Prototypenkette folgen, um den Prototyp der übergeordneten Klasse zu finden, dann das obj-Attribut zu finden und dann obj.a zu ändern. Schauen wir uns die folgende Situation an:
var Child = function(name){
Parent.apply(this,arguments) ;
} ;
var F = neue Funktion(){};
F.prototype = Parent.prototype ;
Child.prototype = new F() ;
var parent = new Parent('myParent') ;
var child = new Child('myChild') ;
console.log(child.obj.a) ; //1
console.log(parent.obj.a); //1
child.obj.a = 2;
console.log(child.obj.a); //2
console.log(parent.obj.a); //2
Hier gibt es ein zentrales Problem. Wenn ein Objekt auf die Eigenschaften im Prototyp zugreift, sind die Eigenschaften im Prototyp für das Objekt schreibgeschützt. Das heißt, das untergeordnete Objekt kann das Objekt obj lesen, aber nicht ändern Das obj-Objekt im Prototyp. Wenn das Kind also obj ändert, hat dies keine Auswirkungen auf das obj-Attribut im Prototyp. Es fügt lediglich ein obj-Attribut zu seinem eigenen Objekt hinzu und überschreibt das obj-Attribut im Prototyp der übergeordneten Klasse. Wenn das untergeordnete Objekt obj.a ändert, liest es zuerst den Verweis auf obj im Prototyp. Zu diesem Zeitpunkt verweisen child.obj und Parent.prototype.obj auf dasselbe Objekt, sodass die Änderung von obj.a durch das untergeordnete Objekt erfolgt beeinflussen den Wert von Parent.prototype.obj.a, der sich wiederum auf das Objekt der übergeordneten Klasse auswirkt. Die Vererbungsmethode der $scope-Verschachtelung in AngularJS wird durch prototypische Vererbung in Javascript implementiert.
Gemäß der obigen Beschreibung tritt die obige Situation auf, solange der Prototyp, auf den im Unterklassenobjekt zugegriffen wird, dasselbe Objekt wie der übergeordnete Klassenprototyp ist. Wir können also den übergeordneten Klassenprototyp kopieren und ihn dann dem Unterklassenprototyp zuweisen. Wenn also eine Unterklasse die Eigenschaften im Prototyp ändert, ändert sie nur eine Kopie des Prototyps der übergeordneten Klasse und hat keine Auswirkungen auf den Prototyp der übergeordneten Klasse. Die spezifische Implementierung lautet wie folgt:
Parent.apply(this,arguments) ;
} ;
Child.prototype = deepClone(Parent.prototype) ;
var parent = new Parent('parent') ;
console.log(parent.obj.a); //1
child.obj.a = '2' ;
console.log(child.obj.a); //2
console.log(parent.obj.a); //1
var extension = function(Parent,Child){
Kind = Kind || function(){} ;
if(Parent === undefiniert)
Rückkehr Kind ;
//借用父类构造函数
Kind = Funktion(){
Parent.apply(this,argument) ;
} ;
//通过深拷贝继承父类原型
Child.prototype = deepClone(Parent.prototype) ;
//重置constructor属性
Child.prototype.constructor = Kind ;
} ;
总结
说了这么多,其实Javascript中实现继承是十分灵活多样的,并没有一种最好的方法,需要根据不同的需求实现不同方式的继承,最重要的是要理解Javascript中实现继承的原理,也就是原型和原型链的问题,只要理解了这些,自己实现继承就可以游刃有余.