Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - Dynamically loading a class and creating new instance with specific properties

I have a Node.js v11.11.0 app. In this app, my files are structured like the following:

./src
  /animals/
    animal.js
    tiger.js
    koala.js
  index.js

As shown above, I have three classes defined in the animals directory. Over time, I intend to add additional animals with more complex logic. At this time, my classes are defined like so:

animal.js

'use strict';

class Animal {
  constructor(properties) {
    properties = properties || {};

    this.kind = 'Unknown';
  }

  eat(foods) {
    for (let i=0; i<foods.length; i++) {
      console.log(`Eating ${foods[i]}`);
    }
  }
}

module.exports = Animal;

tiger.js

'use strict';

const Animal = require('./animal');

class Tiger extends Animal {
  constructor(properties) {
    super(properties);

    this.kind = 'Tiger';
  }

  eat(foods) {
    for (let i=0; i<foods.length; i++) {
      if (foods[i].kind === 'meat') {
        console.log(`Eating ${foods[i]}`);
      }
    }
  }
}

module.exports = Tiger;

koala.js

'use strict';

const Animal = require('./animal');

class Koala extends Animal {
  constructor(properties) {
    super(properties);

    this.kind = 'Koala';
  }

  eat(foods) {
    for (let i=0; i<foods.length; i++) {
      if (foods[i].kind === 'plant') {
        console.log(`Eating ${foods[i]}`);
      }
    }
  }
}

module.exports = Koala;

I am trying to dynamically create an instance of one of these classes based on what the user enters. For example, I'm doing this:

index.js

const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
})

readline.question('What animal file do you want to load?', (fname) => {
  let properties = {
    name: 'My Pet'
  };

  let Animal = require(fname);
  let pet = Object.create(Animal.prototype, properties);
});

If I run this and enter ./animals/tiger.js, I get an error. The error says: Property description must be an object. I don't understand this. Both Tiger and Koala extend Animal. At the same time, as my list will be dynamic, I need the free form option of typing a file path and dynamically loading the class. For that reason, I can't use the approach shown here, which specifically lists the Class names.

How do I dynamically load a class and create an instance of that class with specific properties?

like image 419
user687554 Avatar asked Apr 07 '26 01:04

user687554


1 Answers

The second argument to Object.create is the same argument you would pass to Object.defineProperties, your properties is therefore invalid. Instead use Object.assign:

 Object.assign(Object.create(Animal.prototype), properties)

But why don't you just call the constructor?

 new Animal(properties)

Secondly this:

 properties = properties || {};

is probably intended to be:

 this.properties = properties || {};

And I wouldn't recommend to dynamically require, especially not from a user entered value, instead create a lookup object:

 const AnimalByName = {
   Bear: require("./Bear"),
   //...
 };

 const instance = new AnimalByName[name](properties);
like image 179
Jonas Wilms Avatar answered Apr 09 '26 15:04

Jonas Wilms