在JavaScript(ES6)的构造函数链中调用由子进程覆盖的父函数

and*_*rew 35 javascript constructor class this ecmascript-6

我在下面遇到了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");
  }
}
Run Code Online (Sandbox Code Playgroud)

我期待的是

foo in A is called
foo in B is called
Run Code Online (Sandbox Code Playgroud)

但事实上确实如此

foo in B is called
foo in B is called
Run Code Online (Sandbox Code Playgroud)

我知道我可以通过简单地添加super.foo()B类的foo函数来解决这个问题

class B extends A{
  constructor(){
    super();
    this.foo();
  }
  foo(){
    super.foo() // add this line
    console.log("foo in B is called");
  }
}
Run Code Online (Sandbox Code Playgroud)

但想象一下类似的情景:

Child必须覆盖父项的功能才能执行一些额外的工作,并阻止外部访问能够访问原始功能.

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");
    };
  }
}
Run Code Online (Sandbox Code Playgroud)

在父母的建设中,似乎this仍然指向孩子.这就是为什么我不能达到父母的.BAAfoo

如何this调用构造函数链中被子进程覆盖的父版本函数?

或者在这样的场景中有没有更好的解决方案?

编辑

因此,在阅读答案后,主要问题变为 -

它是气馁放initialize helperssetter functions在JavaScript构造,因为孩子们有机会超越他们?

为了更清楚地澄清情况:(对不起我之前的坏例子:()

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")
Run Code Online (Sandbox Code Playgroud)

this._div会的undefined.

这是一个坏主意,因为孩子能够覆盖这个功能吗?

class A{
  constructor(name){
    this.setName(name); // Is it bad?
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

所以我不应该像在Java,C++中那样使用initialize helperssetter functions构造函数......?

我必须手动调用这样的东西new A().init()来帮我初始化东西吗?

jfr*_*d00 18

您似乎在错误的观念下操作,即有两个对象,A并且B当您在派生类的构造函数中时B.事实并非如此.只有一个对象.无论AB贡献的属性和方法的一个对象.this构造函数中的值将与在构造B函数A中创建B类对象时的值相同.

ES6类语法只是使用原型用于对象类型的ES5方法的糖,事实上,原型仍然在封面下使用.因此,当您foo像在类B中那样在派生类中定义方法时,它仍然会分配给原型,并且该赋值将覆盖来自父定义的原型上可能已存在的任何同名方法.这就是为什么this.foo()指的是B级版本foo.如果您想要达到A类的版本foo,那么您将手动指定使用,super因为您似乎已经知道.

至于你的具体问题:

在父母A中构建时,这似乎仍然指向孩子B.这就是为什么我无法达到父母A的foo.

子B和父A不是单独的对象.父A和子B都有一个对象引用.父A和子B的方法或构造函数将看到完全相同的值this.

如何让它调用在构造函数链中被子进程覆盖的父版本函数?

您可以super直接引用父方法,就像您已经知道的那样.

或者在这样的场景中有没有更好的解决方案?

super 是解决方案.


仅供参考,这是关于ES6课程的一个非常好的讨论,包括如何super工作:ECMAScript 6中的类(最终语义).第4.4节似乎与您的问题/理解特别相关.