skip to Main Content

I’d like to create some kind of factory to automate the process of creating JS classes. The factory should loop through an object that contains classes names and their respective parameters. The following simplified code sample hopefully demonstrates what I mean:

// function for dependency injection
addNumbers = (numbers) => {
    let result = 0;
    
    numbers.forEach((number) => {
        result = result + number;
    })
    
    console.log(`nThe result is ${result}`);
}



// class without parameters
class Log {
    
    constructor() {
        console.log ('My only job is to log this text!n');
    }
}



// class with parameters
class Greeting {
    
    constructor(params) {
        this.sayHello(params);
    }

    sayHello(params) {
        for (const [key, name] of Object.entries(params)) {
            console.log(`Hallo ${name}, nice to meet you!`);
        }
    }
}



// class with parameters
// and dependency injection
class Addition {
    
    constructor(params) {
        this.injectedMethod = params['dependency'];
        this.injectedMethod(params['numbers']);
    }
}



classParams = {

  Log: '',

  Greeting: {
    name1: 'Alice',
    name2: 'Bob'
  },

  Addition: {
    numbers: [1, 2, 3, 4, 5],
    dependency: addNumbers
  }
}



// Here comes the critical part:
// I'd like to have a loop that replaces the 
// following code and iterates through
// classParams, creating the classes with
// their respective parameters
new Log();
new Greeting(classParams['Greeting']);
new Addition(classParams['Addition']);

I tried something like

for (const [object, params] of Object.entries(classParams)) {
    new [object](params);
  }

.. but that won’t work because inside the loop I don’t get the class as an object but only as a string. I tried some things I found online, but the best I could achive was having no errors – but having no working classes either.

What am I getting wrong?

2

Answers


  1. You can put all the classes into an object and use bracket notation to access classes by name using it.

    const addNumbers=e=>{let o=0;e.forEach(e=>{o+=e}),console.log(`
    The result is ${o}`)};class Log{constructor(){console.log("My only job is to log this text!n")}}class Greeting{constructor(e){this.sayHello(e)}sayHello(e){for(let[o,t]of Object.entries(e))console.log(`Hallo ${t}, nice to meet you!`)}}class Addition{constructor(e){this.injectedMethod=e.dependency,this.injectedMethod(e.numbers)}}const classParams={Log:"",Greeting:{name1:"Alice",name2:"Bob"},Addition:{numbers:[1,2,3,4,5],dependency:addNumbers}};
    
    const classes = {Log, Greeting, Addition};
    for (const [k, v] of Object.entries(classParams)) {
      new classes[k](v);
    }
    Login or Signup to reply.
  2. Objects only use strings for keys

    Unfortunately, JavaScript doesn’t have support for values as keys with the Object class. There are, however, many options available to store classes with an argument or arguments to call them with. Below is a non-exhaustive list of such:

    Using an Object and eval to get the class

    This is a safe usage of eval, so no need to worry about that.

    class Example { constructor(arg) { console.log(arg) } }
    const classesToGenerate = {
      Example: 'Hello World'
    }
    for (const className in classesToGenerate) {
      new (eval(className))(classesToGenerate[className])
    }
    

    Using an Array or a Map to store the class itself

    class Example { constructor(arg) { console.log(arg) } }
    let classesToGenerate = new Map([
      [Example, 'Hello World!']
    ])
    classesToGenerate.forEach((arg, Class) => {
      new Class(arg)
    })
    
    // Using an Array is barely different but probably more appropriate
    classesToGenerate = [
      [Example, 'Hello World!']
    ]
    classesToGenerate.forEach(([Class, arg]) => {
      new Class(arg)
    })
    

    Using one Object to store the classes and another to store the arguments

    Credit goes to @Unmitigated for this technique.

    class Example { constructor(arg) { console.log(arg) } }
    const classesToGenerate = {
      classes: { Example },
      args: { Example: 'Hello World' }
    }
    for (const className in classesToGenerate.classes) {
      new classesToGenerate.classes[className](classesToGenerate.args[className])
    }
    

    Sidenote

    If your constructor has multiple parameters or no parameters, I would recommend using an array to hold the arguments and using the spread syntax (i.e. ...) to pass the arguments to the class.

    Also if you need to create multiple of the same class with different arguments, I would recommend the Array technique as shown above.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search