In a current JavaScript project where ES6 class syntax and get/set syntax are used I stumbled upon a behaviour I cannot explain.
First, an extracted demo that works as expected:
class A {
constructor() {
this.__value = null;
}
get value() {
return this.__value;
}
set value(value) {
this.__value = value;
}
}
class B extends A { }
let b = new B();
b.value = 2;
console.log(b.value); // output: 2
Setting and getting b.value (defined in A.prototype) works.
Now consider the following demo in which I moved just the setter from A to B:
class A {
constructor() {
this.__value = null;
}
get value() {
return this.__value;
}
}
class B extends A {
set value(value) {
this.__value = value;
}
}
let b = new B();
b.value = 2; // b.__value is 2
console.log(b.value); // output: undefined
Setting value works (since b.__value is indeed 2), but the getter does not seem to exist although it is still defined in A.prototype.
What is the catch here?
When you try to retrieve a property, and the property isn't on the instance, the engine will look up the prototype chain for the first object in the chain which has a property descriptor for the property in question. When said descriptor is found, if it has a getter, that getter is invoked. Otherwise, if there is no getter, it will retrieve the plain value for that property, if there is one.
In the second case, the property descriptor is on B.prototype
. But B.prototype
does not have a getter for value
(nor does B.prototype
have a plain value for value
)! So, undefined
is returned.
If B.prototype
had a getter for value
, it would be invoked:
'use strict';
class A {
constructor() {
this.__value = null;
}
get value() {
return this.__value;
}
}
class B extends A {
set value(value) {
this.__value = value;
}
get value() {
console.log('trying to get value');
}
}
let b = new B();
b.value = 2;
b.value;
But it doesn't have one. The engine doesn't keep looking for a getter upwards in the prototype chain - instead, it'll just stop and return undefined
, because no getter (or plain value) was found on the first object in the prototype chain which hasOwnProperty('value')
.
If you have a getter, and you want to be able to set that same property, the setter must be on the same object as the getter, and vice-versa.
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