Create Child Class From Parent (ES6 Classes in Javascript) - javascript

What I am trying to do
I am trying to create a child (sub) class by initiating a parent class with the child type as a parameter, and I am wondering how to do this.
For example, say I have the following starter code:
class Animal{
constructor(settings){
//parent value
this.name = settings.name;
}
//parent function
sayName(){
console.log(`My name is ${this.name}.`);
}
}
class Frog extends Animal{
constructor(settings){
super(settings);
//child-specific value
this.isTreeFrog = settings.isTreeFrog;
}
//child function
livesInTheForest(){
return this.isTreeFrog;
}
}
class Rabbit extends Animal{ [...] }
class Whale extends Animal{ [...] }
I want to be able to write:
let barry = new Animal({
animalType: "frog",
name: "Barry",
isTreeFrog: false
})
(rather than let barry = new Frog({name: "Barry", isTreeFrog: false}))
and have barry be a frog, meaning I can write things like this:
barry.sayName() //should print 'My name is Barry'
console.log(barry.livesInTheForest()) //should print 'false'
What I have Tried
I have tried two different ways to achieve this, but both are a bit hacky and don't achieve exactly what I want.
The first involves having a value in the Animal class which stores the child in. For example, in the constructor for Animal I might have something like this:
if(settings.animalType === "frog"){
this.animal = new Frog(settings);
}else [...] //other animal types
There are two main problems with this:
I have to call child functions like this: barry.animal.livesInTheForest(), which creates inconsistency as the parent functions can be called without the .animal.
The child classes (e.g. Frog) can no longer be child classes, as otherwise I will get too much recursion as it keeps trying to call itself with super().
I thought of a second method as well, which works like this:
In the parent (Animal) constructor:
//make sure this isn't being called from the child class
if(settings.animalType !== null){
if(settings.animalType === "frog"){
//set settings.animal null to avoid too much recursion
//this means that I can't set this.animalType though, so I can't access barry.animalType
settings.animalType = null;
//Is this something I can do?!
this = new Frog(settings);
} else [...] //other animal types
}
This works (I think), but I now can't set this.animalType to settings.animalType, meaning I can't write barry.animalType and get frog.
Also, this seems really hacky to me and I can't help thinking that there must be a better way to do this.

class Animal {
static create (settings) {
return new this.subClasses[settings.type](settings)
}
}
class Rabbit extends Animal {}
class Frog extends Animal {}
class Whale extends Animal {}
Animal.subClasses = { frog: Frog, rabbit: Rabbit, whale: Whale }
const animals = ['frog', 'rabbit', 'whale'].map((type) => Animal.create({ type }))
console.log({ animals })

Related

How could one refactor the implementation of two different types of sub classes while keeping both, functionality and naming of their methods?

I have a single base abstract class that expects to be extended, which has defined a sole public method that uses a functionality expected from a subclass. Here, only fullSoundSentence is meant to be "public" - to be used by the outside world.
class Animal {
constructor(nickname){
this.nickname = nickname
}
fullSoundSentence(){
//expects this.composesound to exist
return `${this.nickname} ${this.composesound()}!`
}
}
I then have many classes extending it providing some core functionality (here they simply return a string but in reality they need access to this, modify properties of the object and so on)
class Dog extends Animal {
doessound(){
return "barks"
}
}
class Cat extends Animal {
doessound(){
return "meows"
}
}
Another family of subclasses then uses this functionality, all in a different way. Kind of
"modes" in which we use the Dog and Cat functionalities. Theese modes provide the function expected by the abstract base method (fullSoundSentence), but they need to be a specific animal.
class LoudAnimal extends Animal {
composesound(){
return `loudly ${this.doessound()}`
}
}
class CalmAnimal extends Animal {
composesound(){
return `calmly ${this.doessound()}`
}
}
Now, both cats and dogs can be both calm and loud. = any animal can be in any mode
Q: How do i create a calm dog or a loud cat and so on?
I could create by hand classes for each combination (CalmDog, LoudDog, CalmCat, ...) but if there are more animals and more modes, this is terrible
I could make the modes just object with functionalities that the abstract Animal would expect as an argument for example, but this is messy, especially if the mode also needs to modify properties and so on
Isn't there some better way?? I'm looking for ways to do this in JavaScript or TypeScript.
The next provided approach introduces a toolset of (a) context aware functions and function based mixins (which implement behavior that can not be provided in a meaningful way via a sub-typed class-hierarchy) and (b) an Animal base class, some more specific animal sub types and also a factory in order to show that mixin-based composition can be applied at any time during and at any level of an object creation process (or an object's lifetime).
All implemented functionality got derived from the OP's example code, decomposed and re-assembled while keeping the animal type specific property and method names.
From the OP's originally provided Animal and Dog/Cat extends Animal code, in order to justify the existence of a base Animal class, and also in order to DRY both subtypes (Dog and Cat), such a basic type has to not only feature its nickname but also its sound property and its accompanying doessound method. Dog and Cat both rely on super delegation and are good enough for just providing the default values.
The rest is up to either a mix of further sub typing and mixin composition or to e.g. a factory which does instantiate a class and does apply specific behavior to an instance via mixin composition as well.
Why is that?
Both code lines ...
class LoudAnimal extends Animal { composesound () { /* ... */ } }
... and ...
class CalmAnimal extends Animal { composesound () { /* ... */ } }
... make no sense at all.
A calm/loud animal type is nothing which should/could be described in a meaningful way like in form of an entity which features a nickname and a sound property and a doessound method whereas a dog/cat can be described and recognized by these animal specific keys.
But there can be loud dogs and/or calm cats and/or vice versa. Thus, such types need to acquire specific behavior/traits like being calm/loud or expressing itself calmly/loudly. And this is what mixins are for.
// sound-specific functions/methods (behavior) utilized by mixins.
function getBoundBehaviorDrivenSound(behavior) {
// expects `doessound` to exist but does
// handle it forgiving via optional chaining.
return `${ behavior } ${ this?.doessound?.() }`;
}
function getFullSound() {
// expects `nickname` and `composesound` to exist but
// handles the latter forgiving via optional chaining.
return `${ this.nickname } ${ this?.composesound?.() }!`;
}
// sound-specific function-based mixins (applicable behavior).
function withSoundingLoud() {
this.composesound =
getBoundBehaviorDrivenSound.bind(this, 'loudly');
return this;
}
function withSoundingCalm() {
this.composesound =
getBoundBehaviorDrivenSound.bind(this, 'calmly');
return this;
}
function withFullSound() {
this.fullSoundSentence = getFullSound;
return this;
}
// base-class and sub-typing
class Animal {
constructor({
nickname = 'beast',
sound = 'grmpf',
}) {
Object.assign(this, { nickname, sound });
}
doessound() {
return this.sound;
}
}
class Dog extends Animal {
constructor(nickname = 'Buster') {
super({ nickname, sound: 'barks' });
}
}
class Cat extends Animal {
constructor(nickname = 'Chloe') {
super({ nickname, sound: 'meows' });
}
}
// further sub-typing and mixin based composition.
class LoudDog extends Dog {
constructor(nickname) {
super(nickname);
// mixin at creation time at instance/object level.
withSoundingLoud.call(this);
// withFullSound.call(this);
}
}
// factory function featuring mixin based composition.
function createFullySoundingCalmCat(nickname) {
// mixin at creation time at instance/object level.
return withFullSound.call(
withSoundingCalm.call(
new Cat(nickname)
)
);
}
const smokey = createFullySoundingCalmCat('Smokey');
const cooper = new LoudDog('Cooper');
console.log({ smokey, cooper });
console.log('smokey.doessound() ...', smokey.doessound());
console.log('cooper.doessound() ...', cooper.doessound());
console.log('smokey.composesound() ...', smokey.composesound());
console.log('cooper.composesound() ...', cooper.composesound());
console.log('smokey.fullSoundSentence() ...', smokey.fullSoundSentence());
console.log('cooper?.fullSoundSentence?.() ...', cooper?.fullSoundSentence?.());
// ... one more ...
class FullySoundingLoudDog extends LoudDog {
constructor(nickname) {
super(nickname);
}
}
// prototype level aggregation / "class level mixin".
withFullSound.call(FullySoundingLoudDog.prototype);
const anotherDog = new FullySoundingLoudDog;
console.log({ anotherDog });
console.log('anotherDog.doessound() ...', anotherDog.doessound());
console.log('anotherDog.composesound() ...', anotherDog.composesound());
console.log('anotherDog.fullSoundSentence() ...', anotherDog.fullSoundSentence());
.as-console-wrapper { min-height: 100%!important; top: 0; }

Inability to set value of public class field from constructor of super if that field is redefined in the inheriting class

I use polymorphism with JSDoc to describe concrete implementations of some classes. Up until now I have been doing this with getters and setters; newly implemented public class fields can shorten this getter and setter boilerplate considerably.
My problem is described by the code snippet below. The value set in the constructor does not survive the redefining of the public class field. How can I redefine a public class field in an inheriting class and keep the value set in the super constructor? Is this a bug in public class fields?
class ThingHolder {
/**#type {notKnownYet}*/
publicFieldForThing
constructor(thing) {
this.publicFieldForThing = thing
//Do stuff for all thing holders
}
dropMyThing() {
//Throw not implemented
}
setThingField(thing) {
this.publicFieldForThing = thing
}
}
class RedThingHolder extends ThingHolder {
/**#type {RedThing}*/
publicFieldForThing
dropMyThing() {
//functionality for dropping a red thing with JSDoc niceties for RedThings
}
}
class BlueThingHolder extends ThingHolder {
/**#type {BlueThing}*/
publicFieldForThing
dropMyThing() {
//functionality for dropping a blue thing with JSDoc niceties for BlueThings
}
}
const redThing = {
colour: "red"
}
//Setting the thing in the constructor results in unextected behaviour
const redThingHolder = new RedThingHolder(redThing)
console.assert(redThingHolder.publicFieldForThing === redThing)
//Setting the thing through an inherited method after construction works perfectly fine
redThingHolder.setThingField(redThing)
console.assert(redThingHolder.publicFieldForThing === redThing)
Edit: This question was not about my motivations. Why do public fields behave differently to getters with regard to the prototype chain?
class Parent {
constructor() {
console.log("prop in instance during super: ", "prop" in this)
console.log("publicField in instance during super: ", "publicField" in this)
}
}
class Child extends Parent {
constructor() {
super()
console.log("prop in instance after super: ", "prop" in this)
console.log("publicField in instance after super: ", "publicField" in this)
}
publicField
get prop() { }
}
new Child
When you call the constructor of RedThingHolder class
new RedThingHolder(redThing)
the default constructor of RedThingHolder will pass the parameter redThing to the super class' constructor which will add a property publicFieldForThing on the newly created object.
Re-definition of publicFieldForThing in child classes overwrites the publicFieldForThing set inside the super class.
I feel like this is a bug and that the value should be retained
automatically.
Its not a bug and redefining a property with the same name will not retain a value because you are defining a field with a name that already exists on an object. Instead of retaining, it will overwrite the existing field.
Doing this
class RedThingHolder {
publicFieldForThing;
...
}
will overwrite the publicFieldForThing field with the default value of undefined.
How can I redefine a public class field in an inheriting class and
keep the value set in the super constructor?
You can set the publicFieldForThing in the child class constructor using the getter for publicFieldForThing defined in the super class.
class ThingHolder {
constructor(thing) {
this.publicFieldForThing = thing;
}
get publicField() {
return this.publicFieldForThing;
}
}
class RedThingHolder extends ThingHolder {
constructor(thing) {
super(thing);
this.publicFieldForThing = this.publicField;
}
}
const redThing = {
colour: 'red',
};
const redThingHolder = new RedThingHolder(redThing);
console.log(redThingHolder.publicFieldForThing === redThing);
Edit
Why do public fields behave differently to getters with regard to the
prototype chain?
getter/setters in ES2015's classes are added on the proptotype object whereas the public fields are added on the object itself.
In the code example you posted, prop is added on the Child.prototype whereas the publicField is like doing
this.publicField = undefined;
in the constructor of the Child class just after the super() call.
This is why "prop" in this inside the constructor of the Parent class returns true whereas "publicField" in this evaluates to true only inside the constructor of the Child class.

JavaScript How to get subclasses? [duplicate]

I have code like this
class Animal{}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}
I want to look at all of the classes in my application's universe, and when I find one that descends from Animal, I want to create a new object of that type and add it to the list. This allows me to add functionality without having to update a list of things. So I can avoid the following:
var animals = [];
animals.push( new Dog() );
animals.push( new Cat() );
animals.push( new Donkey() );
PS: I don't want to add extra functionality to my classes or call them explicitly.
I think you could make use of decorators. For instance, you could create #Extends() one and provide base class as an argument, e.g. #Extends(Animal). Inside the decorator function, you could take the name of the class decorated with #Extends and and put it into an array or an object. Don't know if it is applicable in browsers, but it should be. In Node with TypeScript I would do something like:
import { MyClassMetadata } from './';
export function Extends(parent): (...args: any[]) => void {
return (target: object): void => {
MyClassMetadata.someVariableToStoreChildren[parent.constructor.name].push(
target,
);
}
}
Then you can access the MyClassMetadata variable that stores array of children of a given class and use it the way you want. You can play with it and get the desired result.
What about this:
class Animal {
static derived = new Set();
}
class Dog extends Animal {
static dummy = Animal.derived.add(this.name);
}
class Cat extends Animal {
static dummy = Animal.derived.add(this.name);
}
class Donkey extends Animal {
static dummy = Animal.derived.add(this.name);
}
console.log(Animal.derived);
I tried this in a TypeScript environment. The result:
Set(3) {"Dog", "Cat", "Donkey"}
without instantiating a class.
Here what I discovered so far
http://jsbin.com/xiroyurinu/1/edit?js,console,output
class Animal{}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}
var animals = getAllSubclasses(Animal);
console.log(animals.map(function(c){ return new window[c] })) // creates objects
document.body.innerText = animals; // Dog, Cat, Donkey
and the magic
function getAllSubclasses(baseClass) {
var globalObject = Function('return this')();
var allVars = Object.keys(globalObject);
var classes = allVars.filter(function (key) {
try {
var obj = globalObject[key];
return obj.prototype instanceof baseClass;
} catch (e) {
return false;
}
});
return classes;
}
The main disadvantage of this method is that I can not use ES6 module import and have to do old fashioned and simple contatenation of files, but this is still better that nothing.
PS: still wait for better answer
UPD: and ye, i know that to use this all classes must be defined globally, that's why i search for better way to do this..
It is not possible. You can never know e.g. about local classes defined inside some function, or privately in another module. And that's by design. It would be unmodular and break encapsulation.
Also, the set of classes is not static in JavaScript. You can create new classes dynamically open-endedly.
If you think you want such functionality then I strongly suggest you're holding it wrong. What are you trying to achieve?

ES6: Why do extending classes need to explicitly call super? [duplicate]

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
The rules for ES2015 (ES6) classes basically come down to:
In a child class constructor, this cannot be used until super 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.
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.
Source
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());
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.
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.
Just registered to post this solution since the answers here don't satisfy me the least since there is actually a simple way around this. Adjust your class-creation pattern to overwrite your logic in a sub-method while using only the super constructor and forward the constructors arguments to it.
As in you do not create an constructor in your subclasses per se but only reference to an method that is overridden in the respective subclass.
That means you set yourself free from the constructor functionality enforced upon you and refrain to a regular method - that can be overridden and doesn't enforce super() upon you letting yourself the choice if, where and how you want to call super (fully optional) e.g.:
super.ObjectConstructor(...)
class Observable {
constructor() {
return this.ObjectConstructor(arguments);
}
ObjectConstructor(defaultValue, options) {
this.obj = { type: "Observable" };
console.log("Observable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class ArrayObservable extends Observable {
ObjectConstructor(defaultValue, options, someMoreOptions) {
this.obj = { type: "ArrayObservable" };
console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class DomainObservable extends ArrayObservable {
ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
this.obj = super.ObjectConstructor(defaultValue, options);
console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");
cheers!
#Bergi mentioned new.target.prototype, but I was looking for a concrete example proving that you can access this (or better, the reference to the object the client code is creating with new, see below) without having to call super() at all.
Talk is cheap, show me the code... So here is an example:
class A { // Parent
constructor() {
this.a = 123;
}
parentMethod() {
console.log("parentMethod()");
}
}
class B extends A { // Child
constructor() {
var obj = Object.create(new.target.prototype)
// You can interact with obj, which is effectively your `this` here, before returning
// it to the caller.
return obj;
}
childMethod(obj) {
console.log('childMethod()');
console.log('this === obj ?', this === obj)
console.log('obj instanceof A ?', obj instanceof A);
console.log('obj instanceof B ?', obj instanceof B);
}
}
b = new B()
b.parentMethod()
b.childMethod(b)
Which will output:
parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true
So you can see that we are effectively creating an object of type B (the child class) which is also an object of type A (its parent class) and within the childMethod() of child B we have this pointing to the object obj which we created in B's constructor with Object.create(new.target.prototype).
And all this without caring about super at all.
This leverages the fact that in JS a constructor can return a completely different object when the client code constructs a new instance with new.
Hope this helps someone.
Try:
class Character {
constructor(){
if(Object.getPrototypeOf(this) === Character.prototype){
console.log('invoke character');
}
}
}
class Hero extends Character{
constructor(){
super(); // throws exception when not called
console.log('invoke hero');
}
}
var hero = new Hero();
console.log('now let\'s invoke Character');
var char = new Character();
Demo
I would recommend to use OODK-JS if you intend to develop following OOP concepts.
OODK(function($, _){
var Character = $.class(function ($, µ, _){
$.public(function __initialize(){
$.log('invoke character');
});
});
var Hero = $.extends(Character).class(function ($, µ, _){
$.public(function __initialize(){
$.super.__initialize();
$.log('invoke hero');
});
});
var hero = $.new(Hero);
});
Simple solution: I think its clear no need for explanation.
class ParentClass() {
constructor(skipConstructor = false) { // default value is false
if(skipConstructor) return;
// code here only gets executed when 'super()' is called with false
}
}
class SubClass extends ParentClass {
constructor() {
super(true) // true for skipping ParentClass's constructor.
// code
}
}

How to extend a class without having to use super in ES6?

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
The rules for ES2015 (ES6) classes basically come down to:
In a child class constructor, this cannot be used until super 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.
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.
Source
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());
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.
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.
Just registered to post this solution since the answers here don't satisfy me the least since there is actually a simple way around this. Adjust your class-creation pattern to overwrite your logic in a sub-method while using only the super constructor and forward the constructors arguments to it.
As in you do not create an constructor in your subclasses per se but only reference to an method that is overridden in the respective subclass.
That means you set yourself free from the constructor functionality enforced upon you and refrain to a regular method - that can be overridden and doesn't enforce super() upon you letting yourself the choice if, where and how you want to call super (fully optional) e.g.:
super.ObjectConstructor(...)
class Observable {
constructor() {
return this.ObjectConstructor(arguments);
}
ObjectConstructor(defaultValue, options) {
this.obj = { type: "Observable" };
console.log("Observable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class ArrayObservable extends Observable {
ObjectConstructor(defaultValue, options, someMoreOptions) {
this.obj = { type: "ArrayObservable" };
console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class DomainObservable extends ArrayObservable {
ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
this.obj = super.ObjectConstructor(defaultValue, options);
console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");
cheers!
#Bergi mentioned new.target.prototype, but I was looking for a concrete example proving that you can access this (or better, the reference to the object the client code is creating with new, see below) without having to call super() at all.
Talk is cheap, show me the code... So here is an example:
class A { // Parent
constructor() {
this.a = 123;
}
parentMethod() {
console.log("parentMethod()");
}
}
class B extends A { // Child
constructor() {
var obj = Object.create(new.target.prototype)
// You can interact with obj, which is effectively your `this` here, before returning
// it to the caller.
return obj;
}
childMethod(obj) {
console.log('childMethod()');
console.log('this === obj ?', this === obj)
console.log('obj instanceof A ?', obj instanceof A);
console.log('obj instanceof B ?', obj instanceof B);
}
}
b = new B()
b.parentMethod()
b.childMethod(b)
Which will output:
parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true
So you can see that we are effectively creating an object of type B (the child class) which is also an object of type A (its parent class) and within the childMethod() of child B we have this pointing to the object obj which we created in B's constructor with Object.create(new.target.prototype).
And all this without caring about super at all.
This leverages the fact that in JS a constructor can return a completely different object when the client code constructs a new instance with new.
Hope this helps someone.
Try:
class Character {
constructor(){
if(Object.getPrototypeOf(this) === Character.prototype){
console.log('invoke character');
}
}
}
class Hero extends Character{
constructor(){
super(); // throws exception when not called
console.log('invoke hero');
}
}
var hero = new Hero();
console.log('now let\'s invoke Character');
var char = new Character();
Demo
I would recommend to use OODK-JS if you intend to develop following OOP concepts.
OODK(function($, _){
var Character = $.class(function ($, µ, _){
$.public(function __initialize(){
$.log('invoke character');
});
});
var Hero = $.extends(Character).class(function ($, µ, _){
$.public(function __initialize(){
$.super.__initialize();
$.log('invoke hero');
});
});
var hero = $.new(Hero);
});
Simple solution: I think its clear no need for explanation.
class ParentClass() {
constructor(skipConstructor = false) { // default value is false
if(skipConstructor) return;
// code here only gets executed when 'super()' is called with false
}
}
class SubClass extends ParentClass {
constructor() {
super(true) // true for skipping ParentClass's constructor.
// code
}
}

Categories

Resources