class A {
constructor() {
this.x = 1;
}
}
A.prototype.x = 5;
class B extends A {
constructor() {
super();
this.x = 2;
// Super refers to this when it is assigned
super.x = 3;
// When set, super refers to the prototype of the parent class
console.log(super.x); // 5
console.log(this.x); // 3
}
}
let b = new B();
But, why?
When I try to understand these types of cases I like to use babel to see what the code becomes when it is transpiled back to ES5.
When I do that. playground
super.x = 3;
super.x;
becomes roughly:
_set(_getPrototypeOf(B.prototype), "x", 3, this, /* isStrict: */ true);
_get(_getPrototypeOf(B.prototype), "x", this);
Which can be roughly compiled back to 'modern JS' as:
class A {
constructor() {
this.x = 1;
}
}
A.prototype.x = 5;
class B extends A {
constructor() {
super();
this.x = 2;
// Set
Reflect.set(Reflect.getPrototypeOf(B.prototype), "x", 3, this);
// Get
let v = Reflect.get(Reflect.getPrototypeOf(B.prototype), "x", this);
console.log(v); // 5
console.log(this.x); // 3
}
}
void (new B());
So now we no longer have to think about super
, and only look into the difference between Reflect.set and Reflect.get.
(to be continued...)
...continued.
This example can now be reduced to this:
let thePrototypeObject = { x: 5 };
let theReceiverObject = { x: 2 };
Reflect.get(thePrototypeObject, 'x', theReceiverObject); // 5
Reflect.set(thePrototypeObject, 'x', 3, theReceiverObject); // true (it worked)
thePrototypeObject.x; // still 5
theReceiverObject.x; // now 3
Which shows that '[[set]]' can create or update a property on the receiver parameter. But '[[get]]' only uses the receiver when a getter
is encountered.
I think its worth pointing out that MDN does a bad job with the role of receiver with respect to set. It's description for the receiver argument is simply:
The value of this
provided for the call to target
if a setter is encountered.
This ignores the very important fact that when present, if not calling a setter, the receiver becomes the target for the property being set.
const a = { x: 1 }
const b = { x: 2 }
Reflect.set(a, 'x', 3, b) // sets on b, not a
console.log({a, b}) // a: { x: 1 }, b: { x: 3 }}
Especially important in this particular case since that's exactly what's happening with super and this
in:
Reflect.set(Reflect.getPrototypeOf(B.prototype), "x", 3, this); // super.x = 3;
1 Like