web-dev-qa-db-fra.com

Appelez la fonction parent qui est remplacée par l'enfant pendant la chaîne de constructeur en JavaScript (ES6)

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?

35
andrew

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.

18
jfriend00