ECMAScript ne prend en charge que l'héritage d'implémentation, et son héritage d'implémentation repose principalement sur la chaîne de prototypes.
Chaîne prototype
L'idée de base de la chaîne de prototypes est d'utiliser des prototypes pour permettre à un type de référence d'hériter des propriétés et des méthodes d'un autre type de référence. Chaque constructeur possède un objet prototype, l'objet prototype contient un pointeur vers le constructeur et l'instance contient un pointeur vers l'objet prototype. Si : nous rendons l'objet prototype A égal à une autre instance de type B, alors l'objet prototype A aura un pointeur pointant vers l'objet prototype de B, et l'objet prototype correspondant de B stocke un pointeur vers son constructeur. Si l'objet prototype de B est une instance d'un autre type, alors la relation ci-dessus est toujours valable, et ainsi de suite, une chaîne d'instances et de prototypes est formée.
Le schéma de relation entre les instances et les constructeurs et prototypes est le suivant :
person.constructor pointe désormais vers le Parent. En effet, Child.prototype pointe vers le prototype du Parent et le constructeur de l'objet prototype Parent pointe vers le Parent.
Lors de l'accès à une propriété d'instance en mode lecture, la propriété sera d'abord recherchée dans l'instance. Si la propriété n'est pas trouvée, la recherche du prototype de l'instance continuera. En intégration via une chaîne prototype, la recherche se poursuit en remontant la chaîne jusqu'à atteindre le bout de la chaîne.
Par exemple, lors de l'appel de la méthode person.getParentValue(), 1) recherchez des instances ; 2) recherchez Child.prototype ; 3) recherchez Parent.prototype ; arrêtez-vous lorsque la méthode getParentValue() est trouvée.
1. Prototype par défaut
Il manque un lien dans la chaîne de prototypes présentée dans l'exemple précédent. Tous les types de référence héritent de Object par défaut, et cet héritage est également implémenté via la chaîne de prototypes. Par conséquent, le prototype par défaut contient un pointeur interne pointant vers Object.prototype, ce qui constitue la raison fondamentale pour laquelle tous les types personnalisés héritent des méthodes par défaut telles que toString() et ValueOf(). En d’autres termes, Object.prototype est la fin de la chaîne des prototypes.
2. Déterminer la relation entre le prototype et l'instance
La relation entre le prototype et l'instance peut être déterminée de deux manières. La première consiste à utiliser l'opérateur instanceOf et la seconde consiste à utiliser la méthode isPrototypeOf().
Les constructeurs qui apparaissent dans la chaîne de prototypes instanceOf renverront tous true
console.log(person instanceOf Child);//true console.log(person instanceOf Parent);//true console.log(person instanceOf Object);//true isPrototype(),只要是原型链中出现过的原型,都可以说是该原型链所派生出来的实例的原型,因此也返回true. console.log(Object.prototype.isPrototypeOf(instance));//true console.log(Parent.prototype.isPrototypeOf(instance));//true console.log(Child.prototype.isPrototypeOf(instance));//true
3. Définir soigneusement les méthodes
Les sous-types doivent parfois remplacer une méthode dans le supertype, ou doivent ajouter une méthode qui n'existe pas dans le supertype. Remarque : le code pour ajouter des méthodes au prototype doit être placé après l'instruction qui remplace le prototype.
Lorsque getParentValue() est appelée via l'instance Child, cette méthode redéfinie est appelée, mais lorsque getParentValue() est appelée via l'instance Parent, la méthode d'origine est appelée.
Il est important de noter que ces deux méthodes doivent être définies après que l'instance de Parent ait remplacé le prototype.
Un autre point qui nécessite une attention particulière est le suivant : lors de l'implémentation de l'héritage via la chaîne de prototypes, vous ne pouvez pas utiliser de littéraux d'objet pour créer des méthodes prototypes, car cela écraserait la chaîne de prototypes.
Le code ci-dessus attribue simplement l'instance de Parent à l'objet prototype de Child, puis remplace le prototype par un littéral. Après l'avoir remplacé par un littéral, le prototype Child contient en fait une instance d'Object au lieu d'une instance de Parent. , donc la chaîne prototype que nous avions imaginée est rompue. Il n'y a aucun lien entre le parent et l'enfant.
4. Problèmes avec la chaîne du prototype
La chaîne de prototypes est très puissante et peut être utilisée pour implémenter l'héritage, mais il existe également quelques problèmes. Le principal problème est que les propriétés du prototype contenant des valeurs de type référence seront partagées par toutes les instances. Nous définissons donc les propriétés de l'instance dans le constructeur. Mais lorsque l’héritage est implémenté via des prototypes, l’objet prototype devient en réalité une instance d’un autre type. Ainsi, les propriétés d'instance initialement définies dans le constructeur deviennent des propriétés de prototype.
Un exemple est le suivant :
Un attribut friends est défini dans le constructeur Parent, et la valeur de l'attribut est un tableau (valeur de type référence). De cette façon, chaque instance de Parent contiendra son propre attribut amis. Lorsque Child hérite de Parent via la chaîne de prototypes, Child.prototype utilise également l'attribut amis - c'est comme si l'attribut amis était défini dans Child.prototype. De cette façon, toutes les instances de Child partageront l'attribut friends, donc les modifications que nous apportons à kid1.friends seront également reflétées dans kid2.friends. Évidemment, ce n'est pas ce que nous voulons.
Un autre problème avec la chaîne de prototypes est que lors de la création d'une instance d'un sous-type, vous ne pouvez pas transmettre de paramètres au constructeur du supertype sans affecter toutes les instances d'objet. C’est pourquoi nous utilisons rarement la chaîne prototype seule.
Emprunter le constructeur
Afin de résoudre certains problèmes causés par le fait de contenir des valeurs de type référence dans le prototype, la technologie des constructeurs emprunteurs a été introduite. L'idée de base de cette technique est d'appeler le constructeur de supertype à l'intérieur du constructeur de sous-type.
Parent.call(this) appelle le constructeur Parent dans le contexte de l'instance Child nouvellement créée. Appelez le constructeur Parent dans le contexte de l’instance Child nouvellement créée. De cette manière, le code d'initialisation de l'objet défini dans la fonction Parent() est exécuté sur le nouvel objet Child, ici les objets kid1 et kid2. De cette façon, chaque instance Child aura sa propre copie de la propriété friends.
En empruntant un constructeur, les paramètres peuvent être transmis au constructeur de supertype dans le constructeur de sous-type.
Afin de garantir que la familiarité du sous-type ne sera pas remplacée par le constructeur de la classe parent, vous pouvez ajouter les propriétés du sous-type après avoir appelé le constructeur de la classe parent.
Problème de constructeur :
Le problème avec le modèle de constructeur est que les méthodes sont toutes définies dans le constructeur et que la réutilisation des fonctions est impossible. Par conséquent, le modèle qui emprunte le constructeur est rarement utilisé seul.
Héritage combiné
L'héritage combiné fait référence à la combinaison des techniques de chaînage de prototypes et d'emprunt à des constructeurs pour profiter du meilleur des deux mondes. C'est-à-dire : utiliser la chaîne de prototypes pour obtenir l'héritage des propriétés et des méthodes du prototype, et emprunter des constructeurs pour obtenir l'héritage des propriétés d'instance.
Le constructeur Person définit deux attributs : nom et amis. Le prototype de Person définit une méthode sayName(). Lorsque le constructeur Child appelle le constructeur Parent, il transmet le paramètre name, puis définit son propre attribut age. Attribuez ensuite l'instance Person au prototype Child, puis définissez la méthode sayAge() sur le prototype. De cette façon, deux instances Child différentes ont leurs propres attributs, y compris les attributs de type référence, et peuvent utiliser la même méthode.
La combinaison de l'héritage évite les défauts des chaînes de prototypes et des constructeurs, combine leurs avantages et devient le modèle d'héritage le plus couramment utilisé en JavaScript. De plus, instanceOf et isPropertyOf() peuvent également reconnaître les objets créés sur la base d'un héritage combiné.
Enfin, il existe encore plusieurs modes qui n'ont pas été écrits sur les objets JS et l'héritage. En d'autres termes, je ne les ai pas étudiés en profondeur moi-même. Cependant, je pense que je peux d'abord appliquer le mode combinaison. De plus, vous devez savoir pourquoi vous choisissez le mode combinaison et pourquoi.
Concernant plusieurs façons d'implémenter l'héritage en JavaScript (recommandé), l'éditeur vous le présentera ici, j'espère que cela vous sera utile !