super() on class that extends

# Jacob Parker (9 years ago)

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.

# Kyle Simpson (9 years ago)

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:

babeljs.io/repl/#?experimental=false&evaluate=true&loose=false&spec=false&playground=false&code=class A { foo() { console.log("A%3Afoo")%3B } } class B extends A { bar() { super.foo()%3B } } var x %3D new B()%3B x.bar()%3B

# Axel Rauschmayer (9 years ago)

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);
    }
}
# Garrett Smith (9 years ago)

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.
# Sebastian McKenzie (9 years ago)

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

# Axel Rauschmayer (9 years ago)

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.

# Caitlin Potter (9 years ago)

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

# Allen Wirfs-Brock (9 years ago)

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;
   }
} 
# Allen Wirfs-Brock (9 years ago)

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 for length etc.) whose prototype is new.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() {}
}
# Allen Wirfs-Brock (9 years ago)

(typo fix already fixed inline above)

# Sébastien Cevey (9 years ago)

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.

# Erik Arvidsson (9 years ago)

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.

code.google.com/p/v8/issues/detail?id=3886