Javascript's super
keyword, when I run the code on Chrome, Babel, TypeScript, I got different results.
My question is which result is correct? And what part of specification defines such behavior?
The following code:
class Point {
getX() {
console.log(this.x); // C
}
}
class ColorPoint extends Point {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(this.x) // A
console.log(super.x) // B
}
m() {
this.getX()
}
}
const cp = new ColorPoint();
cp.m();
The results:
links:
Chrome is correct. And this is caused by the unbalance between get and set.
OrdinarySet is reciever
sensitive, but OrdinaryGet is not.
So super.x = 3
has the same effect of this.x = 3
, because the receiver here is this
. Evaluatingsuper.x
, which never reach this
, will always get undefined
because A.prototype
does not have such field.
The super.x
is a SuperReference. And assignment to SuperReference
will call PutValue(V, W), which in turn call super
object's internal slot [[Set]] and finally OrdinarySet
.
In plain JavaScript, the statement super.x = 3
is basically equivalent to:
OrdinarySet(proto, 'x', 3, this)
.
Where proto
is the super object, internally the [[HomeObject]]
of constructor ColorPoint
. proto
is equivalent to Object.create(Point.prototype)
, as the ClassDefinitionEvaluation specifies, and it is passed to constructor as [[HomeObject]]
.
Now let's see how OrdinarySet
works. In step 4c and 4d, the spec requires set operation is done on receiver this
, not the proto
object.
Let existingDescriptor be ? Receiver.[GetOwnProperty].
If existingDescriptor is not undefined, then
If IsAccessorDescriptor(existingDescriptor) is true, return false.
If existingDescriptor.[[Writable]] is false, return false.
Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
These statements says OrdinarySet(proto, 3, this)
means this.x = 3
.
On the other hand, OrdinaryGet
ignores Receiver
. super.x
is
OrdinaryGet(proto, 'x', this)
.
OrdinaryGet
does not have Receiver
in its clauses at all! So super.x
is equivalent to Object.create(Point.prototype).x
, which is undefined
of course.
As a rule of thumb, if there is discrepancy between transpilers and browsers, browsers, especially Chrome, are usually more loyal to the ECMAScript specification. Transpilers usually trade some edge case correctness for runtime efficiency.
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