How to extend a class without having to use super in ES6?
Solution 1
The rules for ES2015 (ES6) classes basically come down to:
- In a child class constructor,
this
cannot be used untilsuper
is called. - ES6 class constructors MUST call
super
if they are subclasses, or they must explicitly return some object to take the place of the one that was not initialized.
This comes down to two important sections of the ES2015 spec.
Section 8.1.1.3.4 defines the logic to decide what this
is in the function. The important part for classes is that it is possible for this
be in an "uninitialized"
state, and when in this state, attempting to use this
will throw an exception.
Section 9.2.2, [[Construct]]
, which defines the behavior of functions called via new
or super
. When calling a base class constructor, this
is initialized at step #8 of [[Construct]]
, but for all other cases, this
is uninitialized. At the end of construction, GetThisBinding
is called, so if super
has not been called yet (thus initializing this
), or an explicit replacement object was not returned, the final line of the constructor call will throw an exception.
Solution 2
The new es6 class syntax is only an other notation for "old" es5 "classes" with prototypes. Therefore you cannot instantiate a specific class without setting its prototype (the base class).
Thats like putting cheese on your sandwich without making it. Also you cannot put cheese before making the sandwich, so...
...using this
keyword before calling the super class with super()
is not allowed, too.
// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
constructor() {
super();
this.supplement = "Cheese";
}
}
// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
super();
}
}
// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
}
}
If you don’t specify a constructor for a base class, the following definition is used:
constructor() {}
For derived classes, the following default constructor is used:
constructor(...args) {
super(...args);
}
EDIT: Found this on developer.mozilla.org
:
When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.
Solution 3
There have been multiple answers and comments stating that super
MUST be the first line inside constructor
. That is simply wrong. @loganfsmyth answer has the required references of the requirements, but it boil down to:
Inheriting (extends
) constructor must call super
before using this
and before returning even if this
isn't used
See fragment below (works in Chrome...) to see why it might make sense to have statements (without using this
) before calling super
.
'use strict';
var id = 1;
function idgen() {
return 'ID:' + id++;
}
class Base {
constructor(id) {
this.id = id;
}
toString() { return JSON.stringify(this); }
}
class Derived1 extends Base {
constructor() {
var anID = idgen() + ':Derived1';
super(anID);
this.derivedProp = this.baseProp * 2;
}
}
alert(new Derived1());
Solution 4
You can omit super() in your subclass, if you omit the constructor altogether in your subclass. A 'hidden' default constructor will be included automatically in your subclass. However, if you do include the constructor in your subclass, super() must be called in that constructor.
class A{
constructor(){
this.name = 'hello';
}
}
class B extends A{
constructor(){
// console.log(this.name); // ReferenceError
super();
console.log(this.name);
}
}
class C extends B{} // see? no super(). no constructor()
var x = new B; // hello
var y = new C; // hello
Read this for more information.
Solution 5
The answer by justyourimage is the easiest way, but his example is a little bloated. Here's the generic version:
class Base {
constructor(){
return this._constructor(...arguments);
}
_constructor(){
// just use this as the constructor, no super() restrictions
}
}
class Ext extends Base {
_constructor(){ // _constructor is automatically called, like the real constructor
this.is = "easy"; // no need to call super();
}
}
Don't extend the real constructor()
, just use the fake _constructor()
for the instantiation logic.
Note, this solution makes debugging annoying because you have to step into an extra method for every instantiation.
![xhallix](https://i.stack.imgur.com/zLRld.jpg?s=256&g=1)
xhallix
Updated on May 02, 2020Comments
-
xhallix about 4 years
Is it possible to extend a class in ES6 without calling the
super
method to invoke the parent class?EDIT: The question might be misleading. Is it the standard that we have to call
super()
or am I missing something?For example:
class Character { constructor(){ console.log('invoke character'); } } class Hero extends Character{ constructor(){ super(); // exception thrown here when not called console.log('invoke hero'); } } var hero = new Hero();
When I'm not calling
super()
on the derived class I'm getting a scope problem ->this is not defined
I'm running this with iojs --harmony in v2.3.0
-
xhallix about 9 yearsin this example you also use super() and if you leave it, you have an exception thrown. So I think it is not possible to omit this super() call in this case
-
Jonathan de M. about 9 yearsI thought your goal was not to execute parent constructor, that's what this code does. You can't get rid of super when extending.
-
xhallix about 9 yearssorry for beeing misleading :) No i just wanted to know if it is really necessary to use super() since I wonder about the syntax because in other languages we do not have to invoke the super method when calling the constructor for the derived class
-
zerkms about 9 years"I thought your goal was not to execute parent constructor, that's what this code does." --- it does not. It still executes the parent constructor.
-
Jonathan de M. about 9 yearsAccording to developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…,
A constructor *can* use the super keyword to call the constructor of a parent class.
, so I would say wait for ES6 release -
Jonathan de M. about 9 years@zerkms I meant execute some code in the constructor.
-
zerkms about 9 years"so I would say wait for ES6 release" --- it already was released already, ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
-
xhallix about 9 yearsso if I understand you correctly, I will not be able to use any method from class Character on my class Hero, when not invoking super() ? But this seems not to be fully correct, because I can call the methods from the base class. so I guess I just need super when calling the constructor
-
Amit about 9 years1. In the OP,
this
isn't used at all. 2. JS is not a sandwich, and in ES5 you could always usethis
, even before calling any other function you like (that might or might not define the supplement property) -
marcel about 9 years@amit 1. And now i can't use
this
too?! 2. My JS class represents a sandwich, and in ES6 you cannot always usethis
. I am just trying to explain es6 classes (with a metaphor), and nobody needs such destructive/unnecessary comments. -
Amit about 9 years@marcel My apologies for the cynicism, but: 1. was about introducing a new problem that didn't exist in the OP. 2 is to point your attention that your claim is wrong (Which is still the case)
-
Amit about 9 years@marcel - See my answer
-
marcel about 9 years
"See fragment below (works in Chrome...)"
open the chrome developer console and click on "Run code snippet":Uncaught ReferenceError: this is not defined
. Sure, you can use methods in the constructor beforesuper()
but you cannot use methods from the class before! -
marcel about 9 yearsthat you cannot use
this
beforesuper()
(your code prove it) has nothing to do with the specification immediately, but with the implementation of javascript. So, you have to call 'super' before 'this'. -
Amit almost 9 years@marcel - I think we had a lot of confusion. I was only stating (all along) that it is legal to have STATEMENTS before using
super
, and you were stating that it is illegal to usethis
before callingsuper
. We're both right, just didn't understand each other :-) (And that exception was intentional, to show what isn't legal - I even named the property 'WillFail') -
Bergi almost 9 yearsCould you suggest a way to inherit from a class without calling
super()
? -
loganfsmyth almost 9 years@Bergi Care to clarify?
-
Bergi almost 9 yearsDo you think it's possible in ES6 to inherit from a class without calling
super()
in the constructor? -
Bergi about 8 yearsThanks for the edit - it's right there now; you can do
return Object.create(new.target.prototype, …)
to avoid calling the super constructor. -
Abhishek Avadhoot almost 8 years"Also you cannot put cheese before making the sandwich, so..." Yet oddly we can put cheese on the sandwich without even having cheese. "class Base { constructor () { this.cheese() } } class Derived extends Base { cheese () { console.log('swiss') } } new Derived" actually prints swiss.
-
MyUserInStackOverflow over 7 yearsI'm not sure why all the above boilerplate code, and I'm not exactly sure if there are side effects to this approach. It works for me with no issue.
-
Max Koretskyi about 7 years@Bergi, can you please clarify what's
new.target
? I've never seen such construction used before -
Bergi about 7 years@Maximus See What is “new.target”?
-
swyx almost 7 yearsi need an "explain like i'm five" on this one.. i feel like this is a very deep answer but complicated and there fore ignored
-
cobberboy almost 7 years@swyx : the magic is inside the constructor, where 'this' refers to a different type of object depending on what type of object you're creating. E.g. if you're constructing a new DomainObservable, this.ObjectConstructor refers to a different method i.e. DomainObserveable.ObjectConstructor ; while if you're constructing a new ArrayObservable, this.ObjectConstructor refers to ArrayObservable.ObjectConstructor .
-
kleinfreund over 5 yearsYou should add what happens if the constructor is omitted: A default constructor will be used according to MDN.
-
Michael Lewis over 5 yearsOne issue: if you want to extend your SubClass again, you'll have to build the skipConstructor feature into each SubClass' constructor
-
Michael Lewis over 5 yearsSee my answer, I posted a much simpler example
-
LFLFM over 5 yearsYes, this is by far the easiest method and the clearest answer - the question that I ask is... "Why, God, WHY!?"... There is a very valid reason why super() isn't automatic... you may want to pass specific parameters to your base class, so you can do some processing/logic/thinking before instantiating the base class.
-
spb almost 5 yearsI completely agree @swyx; this answer is doing way too much... i only skimmed it and I'm already tired. I'm feeling like "explain like I'm five AND really have to pee..."
-
vintprox over 4 yearsI like your example, which makes sense to newcomers :)
-
Fawwad over 4 yearsI think when we are extend parent class then the parent class constructor automatically will call in their child class.
-
tonix about 4 years@Bergi
Thanks for the edit - it's right there now; you can do return Object.create(new.target.prototype, …) to avoid calling the super constructor.
Can you provide an example, please? -
x-yuri over 3 yearsI'd say the example is quick and dirty. Sorry for being blunt. It shows that
super
doesn't have to be the first statement. But why would I want that? To generate an id before calling the base constructor? Why not use it for all the classes?derivedProp
isnull
. It at least brings up a lot of questions. And you make it sound like you have an example of practical application. But the example is still theoretical. -
Amit over 3 years@x-yuri this is a 5 years old answer :-) However, I still think its value is in the explanation of what is valid or invalid code, rather than what use case there is for such code. Known more is always a good thing, even if you can't see a use case at any moment in time.