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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With