最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ecmascript 6 - How to call setter of grandparent class in Javascript - Stack Overflow

programmeradmin4浏览0评论

Imagine I have 3 classes Child, Parent and Grandparent connected in hierarchy as follows:

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

How can I call Grandparent's setter of myField in setter of Child class?

I'm interested particularly in setter, not a method. Also it's much preferable to not make changes in Parent of Grandparent classes.

I don't see how that is possible using super because it references just Parent class, as well as using something like Grandparent.prototype.<what?>.call(this, ...) because I don't know what exactly to call in the prototype.

Does anyone have any suggestions for this case?

Thanks in advance!

Imagine I have 3 classes Child, Parent and Grandparent connected in hierarchy as follows:

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

How can I call Grandparent's setter of myField in setter of Child class?

I'm interested particularly in setter, not a method. Also it's much preferable to not make changes in Parent of Grandparent classes.

I don't see how that is possible using super because it references just Parent class, as well as using something like Grandparent.prototype.<what?>.call(this, ...) because I don't know what exactly to call in the prototype.

Does anyone have any suggestions for this case?

Thanks in advance!

Share Improve this question edited Mar 18, 2019 at 23:49 Alena Levina asked Mar 18, 2019 at 19:26 Alena LevinaAlena Levina 1332 silver badges8 bronze badges 4
  • 1 without a distinct ref to it, you can't call it. – dandavis Commented Mar 18, 2019 at 19:31
  • 1 Possible duplicate of How can I call my class' parent's parent's constructor in ES6? – Get Off My Lawn Commented Mar 18, 2019 at 19:32
  • stackoverflow./questions/36370790/… – Get Off My Lawn Commented Mar 18, 2019 at 19:32
  • @GetOffMyLawn thanks for ideas, but my questions is particularly about setter, there is a specific for it which differs it from how same can be acplished for method – Alena Levina Commented Mar 18, 2019 at 23:51
Add a ment  | 

4 Answers 4

Reset to default 8

using something like Grandparent.prototype.<what?>.call(this, ...)

You're on the right track there, you can access the setter method using Object.getOwnPropertyDescriptor:

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

There is a much easier way though: using the Reflect.set helper with a custom receiver:

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

This also has the advantage that it still works when Grandparent doesn't define a setter.


That said, I agree with @Dinu that there's probably a problem with your class hierarchy (or your general design, maybe you shouldn't even use classes or inheritance) when you need to do this.

You can use Reflect.set() with the optional receiver argument like this:

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';

If you don't have an explicit reference to Grandparent.prototype, you can use Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(this))) instead which is obviously a lot less preferable. You could create a helper function though:

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

and use getPrototypeOf(this, 3) in place of Grandparent.prototype to recurse up the prototype chain to the grandparent:

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';

You might find a way to do it, but you shouldn't, not while you're doing class-based OOP, because it breaks the class-based model. In any sane environment, if Parent implements setX(), it expects that setX() behaves the way it defined it, in its context. It does not override setX() so that it can behave the way it did before.

So, if you're asking this, you either designed your class hierarchy wrong, or you actually need the prototype-based OOP.

If you write Parent, I assume what you are trying to obtain is an unmediated access to a certain property. The way to go is to define a method on parent/gradpa, like setXClean(), that is used as a celan setter. In this context, you probably want it to be protected and final on the grandparent.

The easiest way in my opinion, would be to just create another unique setter or method in the Grandparent class that sets 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
  }
}

Another option would be to put super.myField in each setters body:

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
  }
}
发布评论

评论列表(0)

  1. 暂无评论