【问题标题】:Call parent function which is being overridden by child during constructor chain in JavaScript(ES6) [duplicate]在JavaScript(ES6)的构造函数链中调用被子函数覆盖的父函数[重复]
【发布时间】:2015-09-16 17:28:14
【问题描述】:

我在下面的 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 在父A 中构造时仍指向子B。这就是为什么我无法联系到父母Afoo

如何让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")

this._div 将是 undefined

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

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

所以我不应该在构造函数中使用initialize helperssetter functions,比如Java、C++...?

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

【问题讨论】:

  • 如何让this调用父版本函数 - super是唯一的方法。
  • 为什么A 构造函数调用this.foo()?它不应该,它应该只初始化实例。
  • foo 是打算在一般原因中覆盖父实现,还是您更想问如何使“foo”成为一些私人助手?
  • ES6 类语法仍在生成原型,就像我们过去手工编写代码一样。所以,当你在 B 类中定义 foo 时,它仍然会覆盖原型上 foo 的定义,而这个 this.foo() 无论你在哪里调用它都会引用 B 类的定义。您似乎已经知道,如果您想调用 foo 的 A 类版本,您必须手动进行。
  • 仅供参考,您似乎认为有两个对象,一个给孩子,一个给父母。这不是它的工作方式。 this 不指向子 B。this 指向整个对象,其中 A 和 B 都有实例上的属性和原型上的方法。只有一个对象同时具有 A 和 B 贡献的属性和方法。这就是为什么也只有一个 this 以及为什么 this.foo 指向最近的覆盖。

标签: javascript class constructor this ecmascript-6


【解决方案1】:

当您在派生类B 的构造函数中时,您似乎误以为有两个对象AB。根本不是这种情况。只有一个对象。 AB 都为该对象提供属性和方法。在创建 B 类对象期间,this 的值在 B 的构造函数中与在 A 的构造函数中的值相同。

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

至于你的具体问题:

似乎 this 在构造时仍然指向子 B 家长 A。这就是为什么我无法联系到家长 A 的 foo。

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

我如何让它调用正在被调用的父版本函数 在构造函数链期间被子级覆盖?

您使用super 直接引用您似乎已经知道的父方法。

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

super 是解决方案。


仅供参考,这是一个关于 ES6 类的很好的讨论,包括 super 的工作原理:Classes in ECMAScript 6 (final semantics)。第 4.4 节似乎与您的问题/理解特别相关。

【讨论】:

  • 我认为最好不要在父构造函数中使用setter,例如“this.setName(name);”最好使用 this.name = name;像这样,当你使用 super() 时,你不会使用你不知道的覆盖函数。
猜你喜欢
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 2018-01-31
  • 2014-06-09
  • 2014-01-29
  • 1970-01-01
  • 2014-11-20
  • 2021-11-26
相关资源
最近更新 更多