J'ai rencontré un problème ci-dessous avec JavaScript (ES6)
class A{
constructor(){
this.foo();
}
foo(){
console.log("foo in A is called");
}
}
class B extends A{
constructor(){
super();
this.foo();
}
foo(){
console.log("foo in B is called");
}
}
Ce que j'attends c'est
foo in A is called
foo in B is called
Mais en réalité c'est
foo in B is called
foo in B is called
Je sais que je peux résoudre ce problème en ajoutant simplement super.foo()
dans la fonction foo de la classe B.
class B extends A{
constructor(){
super();
this.foo();
}
foo(){
super.foo() // add this line
console.log("foo in B is called");
}
}
Mais imaginons un scénario semblable à celui-ci:
L'enfant doit remplacer la fonction du parent pour pouvoir effectuer des tâches supplémentaires et empêcher l'accès de l'extérieur de pouvoir accéder à celui d'origine.
class B extends A{
constructor(){
super();
this.initBar();
}
foo(){
super.foo();
this.bar.run(); //undefined
console.log("foo in B is called");
}
initBar(){
this.bar.run = function(){
console.log("bar is running");
};
}
}
Il semble que this
pointe toujours sur l'enfant B
lors de la construction dans le parent A
. C'est pourquoi je ne peux pas atteindre le parent de A
de foo
.
Comment faire en sorte que this
appelle la fonction de version parente qui est remplacée par l'enfant pendant la chaîne du constructeur?
Ou existe-t-il une meilleure solution pour ce type de scénario?
Éditer
Donc, après avoir lu les réponses, la question principale devient -
Est-il déconseillé de mettre initialize helpers
Ou setter functions
Dans le constructeur en JavaScript puisque les enfants ont une chance de les ignorer?
Pour clarifier la situation plus clairement: (désolé pour mon mauvais exemple précédent :()
class A{
constructor(name){
this.setName(name);
}
setName(name){
this._name = name;
}
}
class B extends A{
constructor(name){
super(name);
this._div = document.createElementById("div");
}
setName(name){
super.setName(name);
this._div.appendChild(document.createTextNode(name));
}
}
new B("foo")
this._div
Sera undefined
.
Est-ce une mauvaise idée puisque l'enfant pourra remplacer la fonction?
class A{
constructor(name){
this.setName(name); // Is it bad?
}
...
}
Je ne devrais donc pas utiliser initialize helpers
Ou setter functions
Dans un constructeur comme en Java, C++ ...?
Dois-je appeler manuellement des choses comme celle-ci new A().init()
pour m'aider à initialiser les choses?
Vous semblez avoir une idée fausse qu'il existe deux objets A
et B
lorsque vous êtes dans le constructeur de la classe dérivée B
. Ce n'est pas du tout le cas. Il y a un et un seul objet. A
et B
contribuent tous deux des propriétés et des méthodes à cet objet. La valeur de this
sera la même dans le constructeur pour B
que dans le constructeur pour A
lors de la création d'un objet de classe B.
La syntaxe de classe ES6 n’est qu’un bon compromis par rapport à la méthode d’utilisation des prototypes ES5 pour les types d’objets et, en fait, le prototype est toujours utilisé sous les couvertures. En tant que tel, lorsque vous définissez une méthode foo
dans une classe dérivée comme dans la classe B, celle-ci est toujours affectée au prototype et cette affectation remplace toute méthode du même nom pouvant déjà exister sur le prototype qui provenait de la définition parent. C'est pourquoi this.foo()
fait référence à la version de classe B de foo
. Si vous voulez atteindre la version de classe A de foo
, vous devrez alors spécifier manuellement que vous utilisez super
comme vous semblez le savoir déjà.
En ce qui concerne vos questions spécifiques:
Il semble que cela pointe toujours vers l'enfant B pendant la construction du parent A. C'est pourquoi je ne peux pas atteindre le foo du parent A.
l'enfant B et le parent A ne sont pas des objets séparés. Il existe un objet auquel le parent A et l’enfant B font référence. Les méthodes ou les constructeurs parent A et enfant B verront exactement la même valeur que this
.
Comment est-ce que je fais ceci appeler la fonction de version de parent qui est remplacée par l'enfant pendant la chaîne de constructeur?
Vous utilisez super
pour référencer directement les méthodes parentes comme vous semblez le savoir déjà.
Ou existe-t-il une meilleure solution pour ce type de scénario?
super
est la solution.
Pour votre information, ceci est une très bonne discussion sur les classes ES6, y compris comment super
fonctionne: Classes dans ECMAScript 6 (sémantique finale) . La section 4.4 semble particulièrement pertinente pour votre question/compréhension.