-1

I need to create an object from its name in Node.js. How can I do it (without using eval)?

For example, I've tried something like the code bellow, but it failed on `Person is unknown'. I guess it has something with the context (this). I tried the bind/call/apply functions, but with no avail.

module.export = {
    class Person =  {/*....*/};

    var createObject = function(name) {
        return Function('return new ' + name + '();')();
    };

    const p1 = new Person(); // works
    const p2 = eval('return new Person();'); // works, but has security issues
    const p3 = createObject('Person'): // doesn't work.

}:

I’d appreciate it if you could show me how to write the createObject function right.

Ry-
  • 218,210
  • 55
  • 464
  • 476
mov
  • 31
  • 5
  • 3
    What's the purpose of something like this? – Tad Donaghe Mar 13 '18 at 21:45
  • I need to create objects from configuration file. – mov Mar 13 '18 at 21:49
  • 2
    what is the real use case for this? – Nina Scholz Mar 13 '18 at 21:50
  • Again, create objects from configuration file or dynamic object creation – mov Mar 13 '18 at 21:52
  • My guess would be that the stuff that you're passing into `Function` is being run in an isolated context. – pushkin Mar 13 '18 at 21:52
  • What's wrong with `switch`? It's not as though you will get some unknown object type from the config file. – SaganRitual Mar 13 '18 at 21:52
  • From a security perspective, how is `eval` any worse/different than `Function(...)()` ? – Arash Motamedi Mar 13 '18 at 21:53
  • what is the use of `new Foo` or in an other case of `new Bar`, if both required an assignment of the instance? – Nina Scholz Mar 13 '18 at 21:53
  • I don't want to add objects I define to the switch... – mov Mar 13 '18 at 21:54
  • Now I must join all the other responders and ask, why? What use case are you talking about that makes `switch` problematic? Is it just a personal preference, or is there a technical reason you don't want to add objects to a `switch`? – SaganRitual Mar 13 '18 at 21:55
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval – mov Mar 13 '18 at 21:56
  • switch isn't generic and someone can forget to add an object there – mov Mar 13 '18 at 21:57
  • Someone could forget to add an object? You would have the same problem with `eval`. You can't create an object from "Person()" if you haven't already written the code to create a `Person`. – SaganRitual Mar 13 '18 at 21:59
  • Your code is already not generic, because you have to write the specific class anyway. And if you're worried that someone would forget to add an object to the `switch` itself, well, that's what unit testing is for. Write your test to try all possible inputs. – SaganRitual Mar 13 '18 at 22:02
  • Related (maybe even dupe?): https://stackoverflow.com/questions/5117127/use-dynamic-variable-names-in-javascript – Kevin B Mar 13 '18 at 22:05
  • 2
    It appears that it's not worth trying to answer this question because every single answer is immediately downvoted, even perfectly acceptable answers. Something is either busted on stack overflow or there's a nefarious and silent user out there trying to punish anyone who attempts to help. Not sure what's going on here. – jfriend00 Mar 13 '18 at 22:10

4 Answers4

3

Why don't you create a map of creatable objects and lookup against it?

i.e.:

const creatableObjects = {
  'Person': Person,
  'SomethingElse': SomethingElse
}
const config = getConfig();
const createdObject = new (creatableObjects[config.objectName])();
Ry-
  • 218,210
  • 55
  • 464
  • 476
coagmano
  • 5,542
  • 1
  • 28
  • 41
  • shout out to Ryan for fixing my grammatical errors. And shout out to all the other correct answers that got downvoted for who knows what reason. I wasn't even the first answer haha – coagmano Mar 14 '18 at 01:49
1

Create a map of names to constructors:

const permittedTypes = new Map([
    ['Person', Person],
    …
]);

const configuredType = 'Person';

const p1 = new (permittedTypes.get(configuredType));
Ry-
  • 218,210
  • 55
  • 464
  • 476
0

The stuff that you pass into Function doesn't get access to variables defined in the scope from where the constructor is called.

See the source.

Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope. When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the Function constructor was called. This is different from using eval with code for a function expression.

Though I'd be curious to know what your use case is. (I suppose my answer doesn't properly answer your question. It just explains why you're getting an error)

pushkin
  • 9,575
  • 15
  • 51
  • 95
0

Arbitrarily accessing variables using eval() or new Function() can lead to security issues, and isn't all that reliable either when the problem of scoping comes into question.

What you can do instead is use an object with keys mapping to different classes, then reference the object using the specific key you want, so something like this:

const classes = {
  Person: class Person {/* ... */},
  Dog: class Dog {/* ... */},
  Cat: class Cat {/* ... */},
}

function createObject(className) {
  return new classes[className]()
}

const p = createObject('Person') // returns a person

One of the comments also suggested a switch statement, which would work as well. Whatever's cleaner.

kingdaro
  • 11,528
  • 3
  • 34
  • 38
  • This seems like a prefectly reasonable solution (similar to the answer I provided). We need to provide some upvotes here to cancel out the serial downvoters. – jfriend00 Mar 13 '18 at 22:05