【问题标题】:How to call setter of grandparent class in Javascript如何在Javascript中调用祖父母类的setter
【发布时间】:2019-08-09 06:08:00
【问题描述】:

假设我有 3 个类 ChildParentGrandparent 按层次结构连接如下:

class Grandparent {
  set myField(value) {
    console.log('Grandparent setter');
  }
}

class Parent extends Grandparent {
  set myField(value) {
    console.log('Parent setter');
  }
}

class Child extends Parent {
  set myField(value) {
    //I know how to call Parent's setter of myField:
    //super.myField = value;
    //But how to call Grandparent's setter of myField here?
  }
}

如何在Child 类的设置器中调用GrandparentmyField 设置器?

我对 setter 尤其感兴趣,而不是方法。此外,最好不要更改 ParentGrandparent 类。

我不明白使用super 是如何实现的,因为它只引用Parent 类,以及使用Grandparent.prototype.<what?>.call(this, ...) 之类的东西,因为我不知道在原型中究竟要调用什么。

有人对这个案例有什么建议吗?

提前致谢!

【问题讨论】:

标签: javascript ecmascript-6 es6-class


【解决方案1】:

使用类似Grandparent.prototype.<what?>.call(this, ...)

你在正确的轨道上,你可以使用Object.getOwnPropertyDescriptor访问setter方法:

Object.getOwnPropertyDescriptor(Grandparent.prototype, "myField").set.call(this, value);

不过有一个更简单的方法:将Reflect.set helper 与自定义接收器一起使用:

Reflect.set(Grandparent.prototype, "myField", value, this);

这还有一个优点是当Grandparent 没有定义setter 时它仍然有效。


也就是说,我同意@Dinu 的观点,即当您需要这样做时,您的类层次结构(或您的一般设计,也许您甚至不应该使用类或继承)可能存在问题。

【讨论】:

  • 让我知道答案...我非常专注于为递归 Object.getPrototypeOf() 编写辅助函数,以至于我没有注意到这一点!
  • 首选 Object.getOwnPropertyDescriptor 的第一个选项,因为 IE 不支持 Reflect.set。谢谢!
【解决方案2】:

您可以将Reflect.set() 与可选的receiver 参数一起使用,如下所示:

class Grandparent {
  set myField(value) {
    console.log('Grandparent setter');
  }
}

class Parent extends Grandparent {
  set myField(value) {
    console.log('Parent setter');
  }
}

class Child extends Parent {
  set myField(value) {
    Reflect.set(Grandparent.prototype, 'myField', value, this);
  }
}

new Child().myField = 'foo';

如果您没有明确引用 Grandparent.prototype,则可以使用 Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(this))) 代替,这显然不太可取。不过,您可以创建一个辅助函数:

function getPrototypeOf (target, level = 1) {
  return level > 0 ? getPrototypeOf(Object.getPrototypeOf(target), level - 1) : target;
}

并使用getPrototypeOf(this, 3) 代替Grandparent.prototype 将原型链向上递归到祖父母:

function getPrototypeOf (target, level = 1) {
  return level > 0 ? getPrototypeOf(Object.getPrototypeOf(target), level - 1) : target;
}

class Grandparent {
  set myField(value) {
    console.log('Grandparent setter');
  }
}

class Parent extends Grandparent {
  set myField(value) {
    console.log('Parent setter');
  }
}

class Child extends Parent {
  set myField(value) {
    Reflect.set(getPrototypeOf(this, 3), 'myField', value, this);
  }
}

new Child().myField = 'foo';

【讨论】:

  • Reflect.set 完美运行,谢谢!但由于浏览器对 Object.getOwnPropertyDescriptor 的更好支持,不得不选择 Bergi 的答案
  • @AlenaLevina 任何支持class 的浏览器都将支持Reflect
  • 哦,对了,我的错 - 太兴奋了,以至于忘记了我正在使用 babel,反正也没关系 :)
【解决方案3】:

您可能会找到一种方法来做到这一点,但您不应该这样做,而不是在您执行基于类的 OOP 时,因为它破坏了基于类的模型。在任何健全的环境中,如果 Parent 实现 setX(),它期望 setX() 在其上下文中按照它定义的方式运行。它不会覆盖 setX(),因此它可以像以前一样运行。

所以,如果你问这个问题,你要么设计了错误的类层次结构,要么实际上需要基于原型的 OOP。

如果您编写 Parent,我假设您试图获得的是对某个属性的无中介访问。要走的路是在 parent/gradpa 上定义一个方法,如 setXClean(),用作 celan setter。在这种情况下,您可能希望它在祖父母上受到保护和最终。

【讨论】:

  • 我想知道为什么人们会给自己带来困难,而不是创建一个新的类实例,super 很有用,但是 super 太乱了。
  • 这很有用,但感觉更像是评论而不是答案。
  • 如果没有更多关于她为什么需要这个的背景信息,很难给出答案。但为了让它成为一个答案,我试着猜测并编辑了一个添加一个干净的二传手的建议。我不知道谁在编写类树的每个实体,因此不可能在没有设计背景的情况下提出建议……但正如它所制定的那样,它肯定是被误导的,这就是我认为的答案,比如问“我在哪里可以找到会说西班牙语的长颈鹿”?
  • 认为我要问的问题是错误设计的证据是一个好点!不幸的是,我处于需要避免更改父类和祖父类的位置,所以对我来说,只更改子类的答案是可取的
  • 那么可能 AOP 的做事风格(使用反射查看其他人的答案)是唯一的方法。或者直接从祖父母扩展 Child 并将所有 Parent 的代码(不需要的 setter 除外)复制到 Child...
【解决方案4】:

在我看来,最简单的方法是在设置 myField 的祖父类中创建另一个唯一的设置器或方法。

class Grandparent {
  set myField(value) {
    console.log('Grandparent setter');
  }
  set grandParent_myField(value) { this.myField = value }
}

class Parent extends Grandparent {
  set myField(value) {
    console.log('Parent setter');
  }
}

class Child extends Parent {
  set myField(value) {
    this.grandParent_myField = value
  }
}

另一种选择是将super.myField 放入每个setter 正文中:

class Grandparent {
  constructor() { this._myField = null }
  set myField(value) {
    this._myField = value
  }
}

class Parent extends Grandparent {
  set myField(value) {
    super.myField = value
  }
}

class Child extends Parent {
  set myField(value) {
    super.myField = value
  }
}

【讨论】:

  • 感谢您的想法,但对我来说,最好不要在 Parent 或 Grandparent 类中进行更改
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
  • 2022-01-12
  • 2016-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多