thewizardbear
thewizardbear

Reputation: 68

Create Child Class From Parent (ES6 Classes in 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:

  1. I have to call child functions like this: barry.animal.livesInTheForest(), which creates inconsistency as the parent functions can be called without the .animal.
  2. 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.

Upvotes: 0

Views: 1828

Answers (1)

Mestre San
Mestre San

Reputation: 1915

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 })

Upvotes: 2

Related Questions