super() on class that extends
Neither the base (parent) nor derived (child) class requires a constructor, nor does the child class require a super()
call. If you omit either constructor, an assumed one is present. However, if you do declare a constructor in a derived class, you'll need to call super()
in it.
So, to the point of your original question, this is totally valid:
class A {
foo() { console.log("A:foo"); }
}
class B extends A {
bar() { super.foo(); }
}
var x = new B();
x.bar(); // A:foo
See it in action:
The reason why you need to call the super-constructor from a derived class constructor is due to where ES6 allocates instances – they are allocated by/in the base class (this is necessary so that constructors can be subclassed that have exotic instances, e.g. Array
):
// Base class
class A {
// Allocate instance here (done by JS engine)
constructor() {}
}
// Derived class
class B extends A {
constructor() {
// no `this` available, yet
super(); // receive instance from A
// can use `this` now
}
}
// Derived class
class C extends B {
constructor() {
// no `this` available, yet
super(); // receive instance from B
// can use `this` now
}
}
If you do not call super()
, you only get into trouble if you access this
in some manner. Two examples:
// Derived class
class B1 extends A {
constructor() {
// No super-constructor call here!
// ReferenceError: no `this` available
this.foo = 123;
}
}
// Derived class
class B2 extends A {
constructor() {
// No super-constructor call here!
// ReferenceError: implicit return (=access) of `this`
}
}
Therefore, there are two ways to avoid typing super-constructor calls.
First, you can avoid accessing this
by explicitly returning an object from the derived class constructor. However, this is not what you want, because the object created via new B()
does not inherit A
’s methods.
// Base class
class A {
constructor() {}
}
// Derived class
class B extends A {
constructor() {
// No super-constructor call here!
return {}; // must be an object
}
}
Second, you can let JavaScript create default constructors for you:
// Base class
class A {
}
// Derived class
class B extends A {
}
This code is equivalent to:
// Base class
class A {
constructor() {}
}
// Derived class
class B extends A {
constructor(...args) {
super(...args);
}
}
On 4/10/15, Axel Rauschmayer <axel at rauschma.de> wrote:
The reason why you need to call the super-constructor from a derived class constructor is due to where ES6 allocates instances - they are allocated by/in the base class (this is necessary so that constructors can be subclassed that have exotic instances, e.g.
Array
):
Can you please explain how extending Array works. Also what is the optional identifier optional for in ClassExpression:
var myArray = (new class B extends Array {
constructor() {
super(1,2,3,4,5);
}
});
alert(myArray.length); // it's 0 in Babel.
Babel is a terrible reference implementation for subclassing since it relies on the engine. See the docs babeljs.io/docs/usage/caveats/#classes and babel/babel#1172 for more info. This exact question (super in derived class constructors) was also indirectly bought up recently in this issue: babel/babel#1131
No engine has implemented subclassing of Array
, yet: kangax.github.io/compat-table/es6/#Array_is_subclassable
And, as Sebastian mentioned, you can’t transpile it, because it depends on the cooperation of Array
: it becomes the base constructor and allocates an exotic array instance (with special handling for length
etc.) whose prototype is new.target
.
No engine has implemented subclassing of
Array
, yet: kangax.github.io/compat-table/es6/#Array_is_subclassable
v8/v8-git-mirror/blob/master/test/mjsunit/harmony/classes-subclass-arrays.js
Not staged yet, but it is implemented and is very cool. few small pieces missing still
On Apr 10, 2015, at 8:06 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
Therefore, there are two ways to avoid typing super-constructor calls.
or third, about the super class to make sure that you correctly initialize the instance to work with inherited methods:
class B extends A {
constructor(...args) {
let newObj = Reflect.construct(Object, args, this.target);
newObj.prop = something;
return newObj;
}
}
On Apr 10, 2015, at 10:29 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
And, as Sebastian mentioned, you can’t transpile it, because it depends on the cooperation of
Array
: it becomes the base constructor and allocates an exotic array instance (with special handling forlength
etc.) whose prototype isnew.target
.
not totally true:
class SubArray extends Array {
constructor(…args) {
let newObj = new Array(…args);
newObj.__proto__ = SubArray.prototype; //or new.target.prototype
return newObj
}
subclassMethiod() {}
}
(typo fix already fixed inline above)
Does the same apply to subclassing Error?
I've been trying to do that but it doesn't seem to work as expected, in
particular passing the message as argument to super
doesn't seem to do
anything.
For example:
class Foo extends Error {
constructor(message) {
super(message);
}
}
var err1 = new Foo("arg");
console.log(err1); // plain Foo object
console.log(err1.message); // "" (empty string!?)
var err2 = new Error("arg");
console.log(err2); // Error object
console.log(err2.message); // "arg"
The above is true both with Traceur and Babel.
Subclassing Error works in V8 (Chrome 43).
Caitlin, the only issue I know of in V8 regarding subclassing is subclassing Object and the weird wrapper we create.
class X extends Object {}
let x = new X('hi');
print(x); // Oops, we created a String wrapper.
This is because we haven't implemented new.target
for our self hosted
Object
function constructor.
Why was this a requirement? I have a class, we’ll call a, which I want to extend from b, but I don’t want to call the constructor. I just want to inherit a few functions from it.