17

Apart from eval(`o = new ${className}(${args.join(", ")})`), is there any other way to instantiate an object using variable argument list?

E.g.: var foo = instantiate(className, [arg1, arg2, ...])

Samathingamajig
  • 11,839
  • 3
  • 12
  • 34
Alexey Lebedev
  • 11,988
  • 4
  • 39
  • 47

3 Answers3

20

You can instantiate an object with a variable argument list like this:

function instantiate(className, args) {
    var o, f, c;
    c = window[className]; // get reference to class constructor function
    f = function(){}; // dummy function
    f.prototype = c.prototype; // reference same prototype
    o = new f(); // instantiate dummy function to copy prototype properties
    c.apply(o, args); // call class constructor, supplying new object as context
    o.constructor = c; // assign correct constructor (not f)
    return o;
}

Side note: you may wish to pass a direct reference to the class constructor function:

var foo = instantiate(Array, [arg1, arg2, ...]);
// Instead of:
var foo = instantiate("Array", [arg1, arg2, ...]);

... which makes this compatible with non-global functions.

David Tang
  • 92,262
  • 30
  • 167
  • 149
6

Using Object.create() in ES5:

function instantiate(constructor, args) {
    var instance = Object.create(constructor.prototype);
    constructor.apply(instance, args);
    return instance;
}

Using the spread operator in ES6:

var foo = new constructor(...args);
Alexey Lebedev
  • 11,988
  • 4
  • 39
  • 47
  • 1
    Something tells me this will be four times faster than the accepted answer. – Ярослав Рахматуллин Sep 05 '15 at 06:31
  • @inetphantom the first or the second one? – Alexey Lebedev Mar 24 '16 at 16:26
  • The second one. Its just: one not knowing the spread operator will not get what you mean. – inetphantom Mar 24 '16 at 16:37
  • @inetphantom yeah, I see how it can be confusing, but I can't think of anything better than a link to the documentation. Here's a code example: https://jsfiddle.net/upru8scm/ – Alexey Lebedev Mar 25 '16 at 08:57
  • 2
    Downvoted because this answer requires using a type literal, which is antithetical to the question’s premise. – user1944491 Apr 04 '22 at 16:49
  • 1
    @user1944491 what even is a "type literal"? – Alexey Lebedev Apr 04 '22 at 18:17
  • 1
    @AlexeyLebedev In the statement `let m = new Map()` the word Map is a type literal. You can clearly see in the OP’s question that he wants dynamic types, not static predetermined types. – user1944491 Apr 05 '22 at 22:24
  • @user1944491, yeah, I know what the OP is asking about because I'm the OP. In this answer `constructor` is a variable that references the actual constructor. In your example with map it would be: `let constructor = Map;` – Alexey Lebedev Apr 06 '22 at 14:52
  • @user1944491 if what you're saying that according to the question the function should accept a string "Map" instead of the actual Map object, then you're right, this answer is not correct. But in general case going from `"Map"` to `Map` is not possible without eval. – Alexey Lebedev Apr 06 '22 at 14:57
-1

Well you can always do as follows. Anything added to Dino prototype can be shared among the instantiated objects The difference from normal constructor pattern is, the instantiated objects do not have to have the exact same private properties set. They can be set dynamically for each one of them.

function Dino(a,b){
  for(i = 0; i< a.length; i++) this[a[i]] = b[i];
}

var props = ["foo", "bar"],
   values = [42, 37],
      obj = new Dino(props,values);
console.log(obj);
Redu
  • 25,060
  • 6
  • 56
  • 76