3

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?

1

1 Answer 1

5

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);
Sign up to request clarification or add additional context in comments.

3 Comments

+1 for the constructor point. Regarding your last point, what is the point of the lookup object? I don't think about "performances" as OP also requires dynamically. Is it for "UI" reasons (user needs to enter Bear instead of ./animals/Bear.js?
@al-un there are various reasons, one would be the following animal: ../.../../secret/directory/password.json ... additionally there are a lot of cases where the require would crash or create an invalid Animal, which then causes trouble later on.
The new Animal(properties) approach worked. Thank you.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.