The concept of the super
"function call" in JavaScript can be quite perplexing. In normal classes where no objects are returned, the keyword this
simply refers to the current instance of the class being constructed. When calling super()
, it essentially operates on this same reference, resulting in the final constructed object being an instance of the subclass:
class Foo {
a = 10;
constructor() { }
}
class Bar extends Foo {
z: number;
constructor() {
super();
this.z = 1;
}
}
const b = new Bar();
console.log(b); // Bar: {a: 10, z: 1}
console.log(b instanceof Bar); // true
However, if the superclass does return a value from its constructor, then somehow this
in the subclass adopts that value. This allows subsequent references to this
to point to the same object returned by the superclass. As a result, the subclass behaves "normally", although the constructed object is not technically an instance of the class:
class Foo {
constructor() {
return { a: 10 };
}
}
class Bar extends Foo {
z: number;
constructor() {
super();
this.z = 1;
}
}
const b = new Bar();
console.log(b); // {a: 10, z: 1}
console.log(b instanceof Bar); // false
This leads to the idea that when invoking super()
, it's like saying "call the superclass constructor as if it were a function and check if it returns something". If it does, then implicitly assign the result of that function to this
. While you can't directly assign to this
, this process happens automatically behind the scenes. Observing how super constructors work when downleveling code to an earlier version of JS before the introduction of the class
syntax reveals a similar mechanism:
function Bar() {
var _this = _super.call(this) || this;
_this.z = 1;
return _this;
}
In this scenario, the original this
is replaced with a new _this
variable.
Link to playground code