Как вызвать сеттер класса прародителя в Javascript - PullRequest
4 голосов
/ 18 марта 2019

Представьте, что у меня есть 3 класса Child, Parent и Grandparent, соединенных в иерархию следующим образом:

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?
  }
}

Как я могу вызвать сеттер Grandparent myField в сеттерекласса Child?

Меня особенно интересует сеттер, а не метод.Также гораздо предпочтительнее не вносить изменения в Parent из Grandparent классов.

Я не понимаю, как это возможно, используя super, поскольку он ссылается только на класс Parent, а также на использованиечто-то вроде Grandparent.prototype.<what?>.call(this, ...), потому что я не знаю, что именно вызывать в прототипе.

У кого-нибудь есть предложения по этому делу?

Заранее спасибо!

Ответы [ 4 ]

7 голосов
/ 18 марта 2019

используя что-то вроде Grandparent.prototype.<what?>.call(this, ...)

Вы находитесь на правильном пути, вы можете получить доступ к методу установки, используя Object.getOwnPropertyDescriptor:

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

Существует гораздо более простой способ: использование помощника Reflect.set с настраиваемым получателем:

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

Это также имеет то преимущество, что все еще работает, когда Grandparent не определяет сеттер.


Тем не менее, я согласен с @Dinu, что, вероятно, существует проблема с вашей иерархией классов (или вашим общим дизайном, может быть, вам даже не следует использовать классы или наследование), когда вам нужно это сделать.

2 голосов
/ 18 марта 2019

Вы можете использовать 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';
1 голос
/ 18 марта 2019

Вы можете найти способ сделать это, но вы не должны этого делать, пока вы выполняете ООП на основе классов, потому что это нарушает модель на основе классов.В любой разумной среде, если Parent реализует setX (), он ожидает, что setX () ведет себя так, как он его определил, в своем контексте.Он не переопределяет setX (), поэтому он может вести себя так, как раньше.

Итак, если вы спрашиваете об этом, вы либо неправильно спроектировали иерархию классов, либо вам действительно нужен ООП на основе прототипа.

Если вы напишите Parent, я предполагаю, что вы пытаетесь получить прямой доступ к определенной собственности.Путь состоит в том, чтобы определить метод для parent / gradpa, такой как setXClean (), который используется как установщик celan.В этом контексте вы, вероятно, хотите, чтобы он был защищенным и окончательным для деда.

0 голосов
/ 18 марта 2019

Самый простой способ, на мой взгляд, состоит в том, чтобы просто создать еще один уникальный метод или метод в классе Grandparent, который устанавливает 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 в тело каждого сеттера:

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
  }
}
...