如何在 Javascript 中调用祖父母 class 的 setter

How to call setter of grandparent class in Javascript

假设我有 3 个 classes 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 class 的 setter 中调用 GrandparentmyField 的 setter?

我对 setter 特别感兴趣,而不是方法。此外,最好不要对 Grandparent class 的 Parent 进行更改。

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

有人对此案例有什么建议吗?

提前致谢!

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

所以,如果您问这个问题,要么是您的 class 层次结构设计有误,要么您实际上需要 prototype-based OOP。

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

using something like 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 的观点,您的 class 层次结构(或您的总体设计,也许您甚至不应该使用 classes 或继承)可能存在问题,当您需要这样做。

在我看来,最简单的方法是在祖父母 class 中创建另一个独特的 setter 或方法来设置 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
  }
}

您可以将 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';