web-dev-qa-db-ja.com

JavaScript(ES6)のコンストラクターチェーン中に子によってオーバーライドされている親関数を呼び出す

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");
  }
}
_

私が期待するのは

_foo in A is called
foo in B is called
_

しかし、実際には

_foo in B is called
foo in B is called
_

クラスBのfoo関数にsuper.foo()を追加するだけでこれを解決できることを知っています。

_class B extends A{
  constructor(){
    super();
    this.foo();
  }
  foo(){
    super.foo() // add this line
    console.log("foo in B is called");
  }
}
_

しかし、次のようなシナリオを想像してください。

子は、いくつかの余分な作業を行い、外部からのアクセスが元の機能にアクセスできないようにするために、親の機能をオーバーライドする必要があります。

_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");
    };
  }
}
_

thisは、親Bでの構築中に、まだ子Aを指しているようです。そのため、親Afooに到達できません。

コンストラクタチェーン中に子によってオーバーライドされている親バージョン関数を呼び出すthisを作成するにはどうすればよいですか?

または、このようなシナリオになると、より良い解決策はありますか?

編集

したがって、答えを読んだ後、主な質問は次のようになります-

子供がオーバーライドする可能性があるため、JavaScriptのコンストラクタに_initialize helpers_または_setter functions_を配置することは推奨されませんか?

状況をより明確にするために:(前の悪い例はごめんなさい:()

_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_はundefinedになります。

子供は関数をオーバーライドできるので、これは悪い考えですか?

_class A{
  constructor(name){
    this.setName(name); // Is it bad?
  }
  ...
}
_

だから、Java、C++などのコンストラクタで_initialize helpers_または_setter functions_を使用しないでください...?

new A().init()のようなものを手動で呼び出して、初期化を支援する必要がありますか?

35
andrew

派生クラスAのコンストラクターにいるとき、2つのオブジェクトBBがあると誤解して操作しているようです。これはまったく当てはまりません。オブジェクトは1つだけです。 ABは両方とも、その1つのオブジェクトにプロパティとメソッドを提供します。 thisの値は、クラスBのオブジェクトの作成中のBのコンストラクターと同じように、Aのコンストラクターと同じになります。

ES6クラスの構文は、オブジェクトタイプのプロトタイプを使用するES5メソッドの単なるシュガーであり、実際、プロトタイプはまだ裏で使用されています。したがって、クラスBで行うように、派生クラスでメソッドfooを定義する場合、それはまだプロトタイプに割り当てられており、その割り当ては、プロトタイプに既に存在する可能性がある同じ名前のメソッドをオーバーライドします親定義から来ました。これが、this.foo()fooのクラスBバージョンを参照する理由です。 fooのクラスAバージョンに到達したい場合は、すでに知っているように見えるように、superを使用して手動で指定する必要があります。

特定の質問に関して:

これは、親Aでの構築中にまだ子Bを指しているようです。そのため、親Aのfooに到達できません。

子Bと親Aは別個のオブジェクトではありません。親Aと子Bの両方が参照するオブジェクトが1つあります。親Aおよび子Bのメソッドまたはコンストラクターは、thisとまったく同じ値を参照します。

コンストラクタチェーン中に子によってオーバーライドされている親バージョン関数を呼び出すようにするにはどうすればよいですか?

superを使用して、既にわかっているように親メソッドを直接参照します。

または、このようなシナリオになると、より良い解決策はありますか?

superはソリューションです。


参考までに、これはsuperの仕組みを含むES6クラスに関する非常に良い議論です。 ECMAScript 6のクラス(最終セマンティクス) 。セクション4.4はあなたの質問/理解に特に関係があるようです。

18
jfriend00