Dans l'article précédent, le concept de prototype a été introduit et la relation entre les trois bons amis du constructeur, l'objet prototype et l'instance en JavaScript: chaque constructeur a un "gardien" - l'objet prototype, et il y a aussi une "position" du constructeur au cœur de l'objet prototype. Les deux sont amoureux, mais l'instance est "secrètement amoureuse" de l'objet prototype, et elle conserve également la position de l'objet prototype dans son cœur.
JavaScript lui-même n'est pas une langue orientée objet, mais une langue basée sur des objets. Pour ceux qui sont habitués à d'autres langues OO, c'est un peu inconfortable au début car il n'y a pas de concept de "classe" ici, ou il n'y a pas de distinction entre "classe" et "instance", sans parler de la différence entre "Classe parent" et "sous-classe". Alors, comment ces tas d'objets en JavaScript sont-ils liés de cette manière?
Heureusement, JavaScript a fourni une méthode d'implémentation de «succession» au début de sa conception. Avant de comprendre «l'héritage», nous allons maintenant comprendre le concept de chaînes prototypes.
Chaîne prototype
Nous savons que les prototypes ont un pointeur vers le constructeur. Et si nous faisons l'objet Prototype de sous-classe égal à une autre instance de la nouvelle superclasse ()? À l'heure actuelle, l'objet Prototype de sous-classe contient un pointeur vers le prototype Superclass, et le prototype Superclass contient également un pointeur vers le constructeur Superclass. . . De cette façon, une chaîne prototype est formée.
Le code spécifique est le suivant:
fonction superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma girl!"; } function Subclass () {this.subname = "Votre soeur"; } Subsals.prototype = new superclass (); Sous-class.prototype.subsaywhat = function () {return this.subname + ": i`ma belle fille"; } var sub = new SubSlass (); console.log (sub.saywhat ()); // femmes: i`ma girl!Utilisez une chaîne prototype pour obtenir l'héritage
À partir du code ci-dessus, nous pouvons voir que la sous-classe hérite des propriétés et des méthodes de superclasse. Cette implémentation héritée consiste à attribuer l'instance de superclasse à l'objet prototype de la sous-classe. De cette façon, l'objet prototype de la sous-classe est écrasé par une instance de superclasse, ayant toutes ses propriétés et méthodes, et ayant également un pointeur vers l'objet prototype de la superclasse.
Il y a certaines choses qui doivent être prêtées à l'attention lors de la mise en œuvre de l'héritage à l'aide de chaînes prototypes:
Faites attention aux changements dans le constructeur après l'héritage. Ici, le constructeur de Sub pointe vers Superclass car le prototype de sous-classe pointe vers la superclasse. Lorsque vous comprenez la chaîne prototype, n'ignorez pas l'objet par défaut à la fin, c'est pourquoi nous pouvons utiliser des méthodes intégrées telles que ToString dans tous les objets.
Lors de la mise en œuvre de l'héritage via la chaîne prototype, vous ne pouvez pas utiliser la définition littérale de la méthode du prototype, car cela réécrira l'objet prototype (également introduit dans l'article précédent):
fonction superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma girl!"; } function Subclass () {this.subname = "Votre soeur"; } Subsals.prototype = new superclass (); Sous-classe.prototype = {// L'objet Prototype est écrasé ici parce que les attributs et méthodes de superclasse ne peuvent pas être hérités subsaywhat: function () {return this.subname + ": i`ma belle fille"; }} var sub = new SubSlass (); console.log (sub.saywhat ()); // typeError: Undefined n'est pas une fonctionUn problème avec le partage d'instructions. Lors de l'explication du prototype et du constructeur plus tôt, nous avons introduit une fois que les prototypes contenant des attributs de type de référence seront partagés par toutes les instances. De même, les propriétés du type de référence dans le prototype "Classe parent" seront également partagées dans le prototype. Lorsque nous modifions les attributs de type de référence de la "classe parent" via l'héritage du prototype, toutes les autres instances héritées du prototype seront affectées. Cela non seulement gaspille les ressources, mais aussi un phénomène que nous ne voulons pas voir:
fonction superclass () {this.name = "women"; this.bra = ["a", "b"]; } function Subclass () {this.subname = "Votre soeur"; } Subsals.prototype = new superclass (); var sub-1 = new Subslass (); sub1.name = "man"; sub1.bra.push ("C"); Console.log (sub1.name); // man Console.log (sub1.bra); // ["a", "b", "c"] var sub2 = new SubClass (); console.log (sub1.name); // femme console.log (sub2.bra); // ["a", "b", "c"]Remarque: Ajoutez un élément au tableau ici, toutes les instances héritées de Superclass seront affectées, mais si vous modifiez l'attribut de nom, cela n'affectera pas d'autres instances, car le tableau est un type de référence et le nom est un type primitif.
Comment résoudre le problème du partage d'instructions? Continuons à regarder en bas ...
Héritage classique (vol du constructeur)
Comme nous l'avons introduit, nous utilisons rarement des prototypes pour définir les objets seuls, dans le développement réel, nous utilisons rarement des chaînes prototypes seules. Afin de résoudre le problème du partage des types de référence, les développeurs JavaScript ont introduit le modèle d'héritage classique (certaines personnes appellent l'héritage du constructeur emprunté). Son implémentation est très simple à appeler des constructeurs de supertype dans des constructeurs de sous-type. Nous devons utiliser la fonction Call () ou appliquer () fournie par JavaScript, jetons un œil à l'exemple:
fonction superclass () {this.name = "women"; this.bra = ["a", "b"];} function subSlass () {this.subname = "votre soeur"; // attribue la portée de la superclasse au constructeur actuel pour implémenter l'héritage de superclass.call (this);} var sub1 = new Subclass (); sub1.bra.push ("c"); console.log (sub1.bra); // ["a", "b", "c" Subsals (); console.log (sub2.bra); // ["a", "b"]Superclass.Call (this); Cette phrase signifie que le travail d'initialisation du constructeur de superclasse est appelé dans l'environnement de la sous-classe de l'instance (contexte), de sorte que chaque instance aura sa propre copie de l'attribut de soutien-gorge, qui n'aura aucun effet les uns sur les autres.
Cependant, cette méthode d'implémentation n'est toujours pas parfaite. Étant donné que le constructeur est introduit, nous sommes également confrontés au problème avec le constructeur mentionné dans l'article précédent: s'il existe une définition de méthode dans le constructeur, il existe une référence de fonction distincte pour aucune des instances. Notre objectif est de partager cette méthode, et les méthodes que nous définissons dans le prototype SuperType ne peuvent pas être appelées dans l'instance du sous-type:
fonction superclass () {this.name = "women"; this.bra = ["a", "b"]; } Superclass.prototype.saywhat = function () {console.log ("Bonjour"); } function Subclass () {this.subname = "Votre soeur"; Superclass.Call (this); } var sub1 = new SubSlass (); console.log (sub1.saywhat ()); // typeError: Undefined n'est pas une fonctionSi vous avez lu l'article précédent sur les objets et les constructeurs prototypes, vous devez déjà connaître la réponse à la résolution de ce problème, c'est-à-dire suivre la routine de l'article précédent et utiliser "combinaison combinée"!
Héritage combiné
L'héritage combiné est un moyen de combiner les avantages de la chaîne prototype et du constructeur, et de les combiner pour obtenir l'héritage. En termes simples, il s'agit d'utiliser la chaîne prototype pour hériter des attributs et des méthodes, et utiliser des constructeurs empruntés pour implémenter l'héritage des attributs d'instance. Cela résout non seulement le problème du partage d'attributs d'instance, mais permet également à l'héritage d'attributs et de méthodes de super-type:
fonction superclass () {this.name = "women"; this.bra = ["a", "b"]; } Superclass.prototype.saywhat = function () {console.log ("Bonjour"); } function Subclass () {this.subname = "Votre soeur"; Superclass.Call (this); // Le deuxième appel à superclass} subsals.prototype = new SuperClass (); // Le premier appel à Superclass var sub1 = new SubSlass (); console.log (sub1.saywhat ()); //BonjourLa méthode de succession de combinaison est également le moyen le plus couramment utilisé pour mettre en œuvre l'héritage dans le développement réel. À ce stade, il peut répondre à vos besoins de développement réels, mais la poursuite de la perfection par les gens est infinie, donc il y aura inévitablement que quelqu'un "trouvera" à propos de ce modèle: votre modèle a appelé le constructeur de type super! Deux fois. . . L'avez-vous fait? Cette amplification de 100 fois la perte de performance?
La réfutation la plus puissante est de trouver une solution, mais heureusement, le développeur a trouvé la meilleure solution à ce problème:
Héritage de combinaison parasite
Avant d'introduire cette méthode d'héritage, nous comprenons d'abord le concept de constructeur parasite. Le constructeur parasite est similaire au modèle d'usine mentionné ci-dessus. Son idée est de définir une fonction commune. Cette fonction est spécifiquement utilisée pour gérer la création d'un objet. Une fois la création terminée, il renvoie cet objet. Cette fonction est très similaire à un constructeur, mais le constructeur ne renvoie pas de valeur:
fonction gf (name, bra) {var obj = nouvel objet (); obj.name = name; obj.bra = soutien-gorge; obj.saywhat = function () {console.log (this.name); } return obj;} var gf1 = new gf ("bingbing", "c ++"); console.log (gf1.saywhat ()); // bingbingLa mise en œuvre de l'héritage parasite est similaire au constructeur parasite. Il crée une fonction "d'usine" qui ne dépend pas de types spécifiques, traite spécifiquement du processus d'héritage de l'objet, puis renvoie l'instance d'objet hérité. Heureusement, cela ne nous oblige pas à la mettre en œuvre nous-mêmes. Dao GE (Douglas) nous a longtemps fourni une méthode de mise en œuvre:
Fonction Object (obj) {fonction f () {} f.prototype = obj; return new f ();} var superclass = {name: "bingbing", soutienUn constructeur simple est fourni dans une fonction publique, puis une instance de l'objet transmise est attribuée à l'objet prototype du constructeur, et enfin renvoyer une instance du constructeur est très simple, mais l'efficacité est très bonne, n'est-ce pas? Cette méthode est appelée plus tard "l'héritage du prototype", et l'héritage parasitaire est obtenu en fonction du prototype en améliorant les propriétés personnalisées de l'objet:
fonction buildObj (obj) {var o = objet (obj); o.saywhat = function () {console.log ("Bonjour"); } return o;} var superclass = {name: "bingbing", soutienL'héritage parasite fait également face au problème de la réutilisation de la fonction dans les prototypes, alors les gens ont recommencé à rassembler les blocs de construction, et l'héritage de combinaison parasite est né, dans le but de résoudre le problème de l'appel du constructeur de type parent lors de la spécification d'un prototype de sous-type, et en même temps, de maximiser la réutilisation de la fonction. Sur la base des méthodes de mise en œuvre de base ci-dessus, les suivantes sont les suivantes:
// Les paramètres sont deux constructeurs de la fonction INHERITOBJ (sub, sup) {// implémenter l'héritage de l'instance et obtenir une copie du supertype var proto = objet (sup.prototype); // respect de l'attribut constructeur de l'instance proto proto.constructor = sub; // attribue l'objet créé au prototype du sous-type sub.prototype = proto;} fonction superclass () {this.name = "women"; this.bra = ["a", "b"];} superclass.prototype.saywhat = function () {console.log ("bonjour");} fonction subclass () {this.name = "women"; this.bra = ["a", "b"];} superclass.prototype.saywhat = function () {console.log ("bonjour");} fonction subclass () {this.subname = "votre soeur"; SuperClass.Call (this);} INHERITOBJ (sous-classe, Superclass); var sub-1 = new Subclass (); console.log (sub1.saywhat ()); //BonjourCette implémentation évite deux appels vers des supertypes et enregistre également des propriétés inutiles sur la sous-classe.prototype et maintient également la chaîne prototype. À ce stade, le parcours de succession est vraiment terminé, et cette implémentation est également devenue la méthode de mise en œuvre de succession la plus idéale! La controverse des gens sur l'héritage de JavaScript se poursuit. Certains défendent OO, et certains s'opposent à faire des efforts inutiles en JavaScript pour réaliser les caractéristiques de l'OO. Et si c'est le cas, au moins j'ai une compréhension plus profonde!