class Overflow {}
class Stack extends Overflow {
constructor() {
super();
}
}
let stack = new Stack();
https://plnkr.co/edit/JqRfuDAav9opvapwCGpT?p=preview
If I use construtor() I must call super() always.
Why isn't super() auto called in constructor?
Edit: Is super() here calling the base constructor AND setting the prototype? Seems wrong.
Edit: Why is super() required? Even when I've no intention of calling the base constructor.
Why isn't super() auto called in constructor?
So you can decide where in the constructor to call it. This is perfectly valid, for instance:
constructor(foo) {
let bar;
if (foo) {
bar = /*...*/;
} else {
bar = /*...*/;
}
super(bar);
}
...provided no code prior to the call to super uses this (or super.xyz).
Yes, it would have been possible to define the language such that if you didn't have any call to super in your constructor at all, it was added automatically for you at the beginning (a'la Java), but they just decided not to do it that way.
Is super() here calling the base constructor AND setting the prototype? Seems wrong.
No, it's calling the base constructor. The prototype was set before your constructor was called. It's exactly like this old ES5 code:
function Derived() {
Base.call(this);
}
...in:
function Base() {
}
function Derived() {
Base.call(this);
}
Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;
Why is super() required? Even when I've no intention of calling the base constructor.
If you have no intention of calling the base constructor, then your subclass isn't a subclass and shouldn't be extending the base. In a strictly-typed language you'd probably make the base (or a subset of the base) an interface and have your class implement that rather than subclassing. In JavaScript, no need, just make it quack like the duck you want it to be. If an instanceof relationship is important, create a new base that will act like an interface but doesn't do anything anywhere, and have the old base and your class subclass it directly.
Axel FTW
http://www.2ality.com/2015/02/es6-classes-final.html
Summary
Why super is required?
The new keyword class isn't just syntax sugar.
Allocation and instantiation only happens in the base constructor.
But why this?
To allow exotic objects to be extended (thank you Felix Kling).
Related
I currently am learning ES6 classes for Javascript and I seem to understand the concept of them but I don't understand what a derived class inherits from its base class. Is it the methods of the class? Is it safe to assume that the methods of a class are properties of said class? In which case, they are part of the prototype and are thus inherited by objects down the prototype chain. And what about the constructor? Are properties defined inside the constructor inherited?
Thank you for your consideration!
Classes are more or less just syntactic sugar for setting up prototype inheritance.
class Derived extends Base {}
is equivalent to
function Derived(...args) {
return Base.apply(this, args);
}
Object.setPrototypeOf(Derived, Base);
Object.setPrototypeOf(Derived.prototype, Base.prototype);
There is some "magic" involved with super and in the future with public and private class fields, but the basic relationship between objects is the same.
Is it safe to assume that the methods of a class are properties of said class? In which case, they are part of the prototype and are thus inherited by objects down the prototype chain.
Yes, methods become properties of the corresponding prototype object, from which all instances inherit. I.e.
class Foo {
bar() {}
}
is equivalent to
function Foo() {}
Foo.prototype.bar = function() {}
And since a "base class'" property object is in the prototype chain of the derived class, all its methods are available to instances of the derived class.
Are properties defined inside the constructor inherited?
"Inherited" is the wrong word here. The properties are created on the instance itself since that's how constructors work.
Consider the process to be like this:
// Create new instance
var newInstance = Object.create(Derived.prototype);
// Call base constructor with `this` set to new instance
Base.apply(newInstance);
// Call derived constructor with `this` set to new instance
Derived.apply(newInstance);
If the base constructor contained something like this.base = 42;, then that property would be directly created on the new instance, since this refers to the new instance.
Note: In reality the exact flow is a bit different due to the fact extending built-in classes such as Array need special treatment but the end result is roughly the same.
You didn't ask about static methods but these are still part of the inheritance. static methods become properties of the constructor function itself.
class Foo {
static bar() {}
}
is equivalent to
function Foo() {}
Foo.bar = function() {}
Because the constructor of the base class becomes the prototype of the constructor of the derived class, all properties defined on the base constructor are available to the derived constructor.
The developer tools in your browser can actually show you all of this:
class Base {
static staticBase() {}
constructor() {
this.base = 42;
}
fromBase() {}
}
class Derived extends Base {
static staticDervied() {}
constructor() {
super(); // necessary since we extend base
this.derived = 21;
}
fromDerived() {}
}
console.dir(Derived);
console.dir(new Derived());
there are a lot of great resource about fundamental es06
example - https://exploringjs.com/es6/ch_classes.html#details-of-subclassing
I'm looking through the MDN docs on classes, here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
but when playing with an example noticed that the output was identical even though I deleted the constructor in the subclass. I would have thought that the new instance of dog wouldn't be able to pass its property to the super class as it didn't use super(props) in its class definition
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
// constructor(name) {
// super(name);
// }
speak() {
console.log(`${this.name} barks.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // "Mitzie barks" even though not calling super class constructor... or are we?!
Any help appreciated
When you don't specify a constructor at all, the JavaScript engine inserts a default one for you. In a base class, the default looks like this:
constructor() {
}
In a subclass, it look like this:
constructor(...args) {
super(...args);
}
As you can see, in a subclass the default constructor passes along all of its arguments to the superclass constructor, which explains why Mitzie barks. :-)
See Step 10 of Runtime Semantics: ClassDefinitionEvaluation.
If you omit it, you get a default constructor that just calls the superclasses constructor with the arguments.
So it isn't needed … unless you want the override the constructor so that the subclass does something different or extra when you construct it.
I want to pass child class instance to super class using super's constructor but I'm get this error
super(this);
this is not allowed before superclass constructor invocation
Why i'm getting this error , also how could I resolve this issue
class Parent
{
constructor(child)
{
this.child = child;
}
//...somewhere in code
//child.doSomething();
}
class Child extends Parent
{
constructor()
{
super(this); // <==== the error here
}
doSomething = () =>
{
//...
}
}
There's no need to pass this to super() because this inside the superclass constructor will be a reference to the same object. Recall that your class hierarchy will cooperate to perform initialization on a single new object.
Calls to super() must come before any reference to this, including in the super() argument list. Why? Because in order to mimic behavior of other OO languages, it must be the case that the top-most initializer in the class hierarchy gets its hands on the new object first. The parent (or "senior") initializer should be able to assume that prototype methods at that level have the semantics the base class expects, since it doesn't "know" what subclasses might have done with their prototypes etc. If a subclass initializer could modify the new object and override a base class prototype method (or something else of that flavor), it'd be chaos.
In the child's constructor method, the parent's constructor must be called before accessing this. The superclass' constructor will have access to this anyway, since you are constructing an instance of the superclass - though it will not have access to any other initialization that your child constructor may yet do.
Before ES6 classes, a function could be used as a constructor:
function MyClass(a, b) {
}
Then, the following code is equivalent to a classic instantiation (like let thisObj = new MyClass("A", "B")):
let thisObj = Object.create(MyClass.prototype)
// Here we know the `this` object before to call the constructor.
// Then, the constructor is called manually:
MyClass.call(thisObj, "A", "B")
… This technique was a way to know the this object before calling the constructor. But Function.prototype.call() doesn't work on an ES6 class constructor.
With ES6 we have Reflect.construct():
let thisObj = Reflect.construct(MyClass, "A", "B");
But it doesn't provide a way to call the constructor after the this object is created.
Is it still possible to do that with ES6 classes?
My use case
I would have needed to keep this feature from ES5 to ES6 for a framework. The framework is responsible for instantiating components (which are ES6 classes). A component can create child components (in a tree of components, there is no inheritance here) from its constructor. Then, a child component can query the framework to get its parent from its own constructor. In this case, we have a technical limitation because the framework still doesn't have the return value of the parent component constructor. This is a regression compared to (a transpilation to) ES5.
It's impossible to do that with ES6 classes. ES6 classes are supposed to be instantiated only with new or Reflect.construct.
Function-calling classes is currently forbidden. That was done to keep options open for the future, to eventually add a way to handle function calls via classes. [source: exploringjs]
See also:
Is it possible to inherit old-style class from ECMAScript 6 class in JavaScript?
are es6 classes just syntactic sugar for the prototypal pattern in javascript?
Why this pattern is not viable
Class instance isn't necessary this object that appears in constructor, because ES6 class can return a value from constructor, which is considered class instance:
class Foo {
constructor {
// `this` is never used
return {};
}
}
A component can create sub-components from its constructor. Then, a sub-component can query the framework to get its parent from its own constructor
This pattern is not viable in ES6 classes. The limitation restricts this from appearing before super.
In order to reach particular classes in hierarchy, decorator pattern can be used to decorate a class when it's defined. This allows to modify its prototype on definition or put an intermediate class between a parent and a child. This approach solves a lot of framework-specific tasks like dependency injection and is used in modern frameworks (Angular, etc.).
A convenient way to do this is ECMAScript Next/TypeScript decorator. Here's an example that shows that a decorator allows to dynamically intercept child constructor and augment child prototype:
let BAZ;
class Foo {
constructor(foo) {
console.log(foo);
}
}
function bazDecorator(Class) {
return class extends Class {
constructor(foo) {
super(BAZ || foo);
}
get bar() {
return BAZ || super.bar;
}
}
}
#bazDecorator
class Bar extends Foo {
constructor(foo) {
super(foo);
console.log(this.bar);
}
get bar() {
return 'bar';
}
}
// original behaviour
new Bar('foo'); // outputs 'foo', 'bar'
// decorated behaviour
BAZ = 'baz';
new Bar('foo'); outputs 'baz', 'baz'
ES.next decorator is basically a helper function. Even without syntactic sugar, it is still applicable in ES6 with slightly different syntax:
const Bar = bazDecorator(
class Bar extends Foo { ... }
);
When running the following code on Node.js 4.2.1:
'use strict';
var util = require('util');
class MyClass {
constructor(name) {
this.name = name;
}
}
function MyDerived() {
MyClass.call(this, 'MyDerived');
}
util.inherits(MyDerived, MyClass);
var d = new MyDerived();
I get the following error:
constructor(name) {
^
TypeError: Class constructors cannot be invoked without 'new'
I wonder if it is at all possible to inherit old-style JavaScript "classes" from ECMAScript 6 classes? And, if possible, then how?
There's not really a way out once you've opted in to class syntax.
The problem is that inheritance in ES6 is done by late-initialising the this keyword with the return value from super(), which is a constructor call like with new. The old idiom of "applying" (.call()) the parent constructor on the currently "uninitialised" child instance does work no more.
What you can still do is to resort to "parasitic inheritance" - you'll have to explicitly construct the instance, extend it, and return it:
function MyDerived() {
var derived = new MyClass('MyDerived');
… // set up properties
return derived;
}
When you do this with new MyClass, you won't get the prototype set up correctly however. For that, you will need to use the new ES6 Reflect.construct function, which takes the child class as an optional third parameter:
function MyDerived() {
var derived = Reflect.construct(MyClass, ['MyDerived'], new.target||MyDerived);
… // set up properties
return derived;
}
This has the additional benefit that MyDerived doesn't need to be called with new any more (as long as you supply MyDerived when new.target is empty).