Cet article partage avec vous une introduction détaillée à l'héritage JS (chaîne de prototypes, constructeur, combinaison, prototype, parasite, combinaison parasite, extensions de classe). Il a une certaine valeur de référence pour les amis dans le besoin.
Pour être honnête, dans le passé, j'avais seulement besoin de savoir que "l'héritage combinatoire parasite" était le meilleur, à condition qu'il y ait un modèle de code ancestral à utiliser. Depuis quelques semaines, je réfléchis à mettre certaines choses au point. Cet article utilise le contenu de "JavaScript Advanced Programming" comme squelette, complète le contenu pertinent de la classe ES6 et décrit l'héritage dans une perspective que je pense plus facile à comprendre. J'espère que tout le monde pourra y gagner quelque chose.
Donnons d'abord une impression générale. Comme le montre la figure, l'héritage dans JS peut être divisé en deux parties selon que la fonction objet est utilisée ou non (mentionnée ci-dessous) (Object.create est une nouvelle méthode dans ES5 pour standardiser cette fonction).
Parmi eux, l'héritage de chaîne de prototypes et l'héritage de prototypes ont les mêmes avantages et inconvénients, et l'héritage de constructeur et l'héritage parasite se correspondent également. L'héritage de combinaison parasite est basé sur Object.create, et en même temps il optimise l'héritage de combinaison et devient une méthode d'héritage parfaite. Le résultat de ES6 Class Extends est fondamentalement le même que celui de l'héritage de combinaison parasite, mais le schéma d'implémentation est légèrement différent.
Entrons dans le vif du sujet tout de suite.
L'héritage de chaîne de prototype, l'héritage de constructeur et l'héritage de combinaison dans la moitié supérieure de l'image ci-dessus ont beaucoup du contenu sur Internet. Cet article ne le décrit pas en détail, mais en souligne seulement les points clés. Voici l'article "Héritage en JS (Partie 1)" qui me semble le plus simple à comprendre. Si vous n'êtes pas familier avec le contenu de la première moitié, vous pouvez d'abord lire cet article puis revenir pour continuer la lecture si vous le connaissez déjà, vous pouvez rapidement ignorer cette partie ; De plus, la première moitié de la section emprunte beaucoup à un article hérité sur le front-end yq [1].
Core : Utiliser l'instance de la classe parent comme prototype de la classe enfant
SubType.prototype = new SuperType() // 所有涉及到原型链继承的继承方式都要修改子类构造函数的指向,否则子类实例的构造函数会指向SuperType。 SubType.prototype.constructor = SubType;
Avantages : La méthode de la classe parent peut être réutilisée
Inconvénients :
Les attributs de référence de la classe parent seront partagés par toutes les instances de sous-classe
Les sous-classes ne peuvent pas transmettre de paramètres à la classe parent lors de la construction d'instances
Core : copiez le contenu du constructeur de la classe parent dans le constructeur de la sous-classe. C'est le seul héritage parmi tous les héritages qui n'implique pas de prototype.
SuperType.call(SubType);
Avantages : Complètement opposé à l'héritage de chaîne de prototype.
Les propriétés de référence de la classe parent ne seront pas partagées
La sous-classe peut transmettre des paramètres à la classe parent lors de la construction d'une instance
Inconvénients : Les méthodes de la classe parent ne peuvent pas être réutilisées et les méthodes de l'instance de sous-classe sont créées séparément à chaque fois.
Core : Une combinaison d'héritage prototypique et d'héritage constructeur, qui combine les avantages des deux.
function SuperType() { this.name = 'parent'; this.arr = [1, 2, 3]; } SuperType.prototype.say = function() { console.log('this is parent') } function SubType() { SuperType.call(this) // 第二次调用SuperType } SubType.prototype = new SuperType() // 第一次调用SuperType
Avantages :
Les méthodes de la classe parent peuvent être réutilisées
Les attributs de référence de la classe parent la classe ne sera pas partagée
Les sous-classes peuvent transmettre des paramètres à la classe parent lors de la construction d'instances
Inconvénients :
Appelle le constructeur du parent class fonctionne deux fois, la première fois les attributs name et arr de la classe parent sont ajoutés au prototype de la sous-classe, et la deuxième fois les attributs name et arr de la classe parent sont ajoutés au constructeur de la sous-classe, remplaçant ainsi le paramètres du même nom dans le prototype de la sous-classe. Cette situation d'écrasement entraîne un gaspillage de performances.
Core : La méthode objet de l'héritage prototypique est essentiellement une copie superficielle de l'objet paramètre.
Avantages : les méthodes de la classe parent peuvent être réutilisées
Inconvénients :
Les propriétés de référence de la classe parent seront partagées par toutes les instances de sous-classe
Les sous-classes ne peuvent pas transmettre de paramètres aux classes parents lors de la construction d'instances
function object(o){ function F(){} F.prototype = o; return new F(); } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
ECMAScript 5 standardise l'héritage prototypique via la nouvelle méthode Object.create(). Cette méthode accepte deux paramètres : un objet à utiliser comme prototype du nouvel objet et (éventuellement) un objet pour définir des propriétés supplémentaires pour le nouvel objet. La méthode Object.create() se comporte de la même manière que la méthode object() lorsqu'un paramètre est transmis. ——"JAVASCript Advanced Programming"
Ainsi, le code ci-dessus peut être transformé en
var yetAnotherPerson = object(person); => var yetAnotherPerson = Object.create(person);
Core : Utiliser l'héritage prototypique pour obtenir un objet cible Shallow copier, puis améliorer la capacité de cette copie superficielle.
Avantages et inconvénients : ne fournit qu'une seule idée, aucun avantage
function createAnother(original){ var clone=object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象 } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
Comme mentionné tout à l'heure, l'un des héritages de combinaison appellera le constructeur du parent classe deux fois. L'inconvénient du gaspillage est que l'héritage combinatoire parasite peut résoudre ce problème.
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); // 创建了父类原型的浅复制 prototype.constructor = subType; // 修正原型的构造函数 subType.prototype = prototype; // 将子类的原型替换为这个原型 } function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); this.age = age; } // 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费 inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); }
Avantages et inconvénients : C'est un mode d'héritage parfait.
Core : Les résultats de l'héritage ES6 sont similaires à l'héritage combiné parasite. Essentiellement, l'héritage ES6 est une sorte de sucre syntaxique. Cependant, l'héritage combiné parasite crée d'abord l'objet this de l'instance de sous-classe, puis l'améliore ; tandis qu'ES6 y ajoute d'abord les attributs et les méthodes de l'objet d'instance de classe parent (la super méthode doit donc être appelée en premier), puis utilise l'objet d'instance de classe parent. objet d'instance de sous-classe vers Le constructeur de la classe modifie cela.
class A {} class B extends A { constructor() { super(); } }
Le principe spécifique de l'héritage en ES6 :
class A { } class B { } Object.setPrototypeOf = function (obj, proto) { obj.__proto__ = proto; return obj; } // B 的实例继承 A 的实例 Object.setPrototypeOf(B.prototype, A.prototype); // B 继承 A 的静态属性 Object.setPrototypeOf(B, A);
ES6继承与ES5继承的异同:
相同点:本质上ES6继承是ES5继承的语法糖
不同点:
ES6继承中子类的构造函数的原型链指向父类的构造函数,ES5中使用的是构造函数复制,没有原型链指向。
ES6子类实例的构建,基于父类实例,ES5中不是。
ES6 Class extends是ES5继承的语法糖
JS的继承除了构造函数继承之外都基于原型链构建的
可以用寄生组合继承实现ES6 Class extends,但是还是会有细微的差别
相关推荐:
AngularJs自定义指令可以如何来设置以及自定义指令的命名规范
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!