David David - 26 days ago 8
Javascript Question

Storing an ES6 Javascript class's getter/setter when inheritance is involed

Edit: The reason I am doing the below process is so I can store the reference to the getter/setter in a dictionary. This allows me to have the key of my dictionary be an ID of an HTML element. Thus, if a property is changed in the HTML, I can do something like:

var propData = allMyGetterSetters[e.originalTarget.id];
propData.getSet.set(propData.obj, e.originalTarget.value);


This also allows me to do a loop and update all the HTML, should my logic change it.




I need to store a reference to the getter/setters of a few properties of one of classes. I've managed to do this with the following code:

Object.getOwnPropertyDescriptor(Object.getPrototypeOf(myClassObj.position), "x");


For simplicity, since I have to do this several times, I have the following method:

_makeGetSetObj(object, property){
return {
getSet: Object.getOwnPropertyDescriptor(Object.getPrototypeOf(object), property),
obj: object
};
}


And subsequent code would look something like this:

var xPos = this._makeGetSetObj(myClassObj.position, "x");
// ...
xPos.getSet.get(xPos.obj);


All of this works perfectly.

However, I now need to store a reference to a getter/setter of my
myclassObj
object. However, the following does not work

this._makeGetSetObj(myClassObj, "name");


This actually gives me an error that
name
does not exist on the object. I've managed to figure out that the problem is my inheritance, which looks something like this

|-- BaseClass
|-- MyClass
|-- DerivedClass


The problem seems to be that
myClassObj
is actually an object of type
DerivedClass
, which doesn't have the property
name
on it.

So, if I do this:

this._makeGetSetObj(myClassObj.__proto__, "name");


It works to get the prototype, but when I try to use it as shown above (with my
xPos
example), it fails because it seems to still be storing an reference in
obj
as a
DerivedClass
object.

If I pull outside of my method, and try things manually, this works:

var name = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(myClassObj.__proto__), "name");
name.get(myClassObj);


This obviously breaks my method though, as one part requires the
__proto__
while the other part does not.

So, my question is: Is there a way to keep my current logic, or will I have to create a special method for the places with the described problem?

Thanks.

Answer

Hardcoded prototype doesn't smell good. Prototype chains should always be iterated:

let proto = obj;
let descriptor;
do {
  descriptor = Object.getOwnPropertyDescriptor(proto, prop);
} while (!descriptor && proto = Object.getPrototypeOf(proto))
...

This functionality has been already implemented by Reflect. Instead of parsing descriptors manually, it may be

const getSet = {
  get: () => Reflect.get(obj, prop),
  set: (val) => { Reflect.set(obj, prop, val) }
};

Or... just

const getSet = {
  get: () => obj[prop],
  set: (val) => { obj[prop] = val }
};

Because this is what happens when accessors are called directly.