Use of .apply() with 'new' operator. Is this possible?

81,283

Solution 1

With ECMAScript5's Function.prototype.bind things get pretty clean:

function newCall(Cls) {
    return new (Function.prototype.bind.apply(Cls, arguments));
    // or even
    // return new (Cls.bind.apply(Cls, arguments));
    // if you know that Cls.bind has not been overwritten
}

It can be used as follows:

var s = newCall(Something, a, b, c);

or even directly:

var s = new (Function.prototype.bind.call(Something, null, a, b, c));

var s = new (Function.prototype.bind.apply(Something, [null, a, b, c]));

This and the eval-based solution are the only ones that always work, even with special constructors like Date:

var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true

edit

A bit of explanation: We need to run new on a function that takes a limited number of arguments. The bind method allows us to do it like so:

var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();

The anything parameter doesn't matter much, since the new keyword resets f's context. However, it is required for syntactical reasons. Now, for the bind call: We need to pass a variable number of arguments, so this does the trick:

var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();

Let's wrap that in a function. Cls is passed as argument 0, so it's gonna be our anything.

function newCall(Cls /*, arg1, arg2, ... */) {
    var f = Cls.bind.apply(Cls, arguments);
    return new f();
}

Actually, the temporary f variable is not needed at all:

function newCall(Cls /*, arg1, arg2, ... */) {
    return new (Cls.bind.apply(Cls, arguments))();
}

Finally, we should make sure that bind is really what we need. (Cls.bind may have been overwritten). So replace it by Function.prototype.bind, and we get the final result as above.

Solution 2

Here's a generalized solution that can call any constructor (except native constructors that behave differently when called as functions, like String, Number, Date, etc.) with an array of arguments:

function construct(constructor, args) {
    function F() {
        return constructor.apply(this, args);
    }
    F.prototype = constructor.prototype;
    return new F();
}

An object created by calling construct(Class, [1, 2, 3]) would be identical to an object created with new Class(1, 2, 3).

You could also make a more specific version so you don't have to pass the constructor every time. This is also slightly more efficient, since it doesn't need to create a new instance of the inner function every time you call it.

var createSomething = (function() {
    function F(args) {
        return Something.apply(this, args);
    }
    F.prototype = Something.prototype;

    return function(args) {
        return new F(args);
    }
})();

The reason for creating and calling the outer anonymous function like that is to keep function F from polluting the global namespace. It's sometimes called the module pattern.

[UPDATE]

For those who want to use this in TypeScript, since TS gives an error if F returns anything:

function construct(constructor, args) {
    function F() : void {
        constructor.apply(this, args);
    }
    F.prototype = constructor.prototype;
    return new F();
}

Solution 3

If your environment supports ECMA Script 2015's spread operator (...), you can simply use it like this

function Something() {
    // init stuff
}

function createSomething() {
    return new Something(...arguments);
}

Note: Now that the ECMA Script 2015's specifications are published and most JavaScript engines are actively implementing it, this would be the preferred way of doing this.

You can check the Spread operator's support in few of the major environments, here.

Solution 4

Suppose you've got an Items constructor which slurps up all the arguments you throw at it:

function Items () {
    this.elems = [].slice.call(arguments);
}

Items.prototype.sum = function () {
    return this.elems.reduce(function (sum, x) { return sum + x }, 0);
};

You can create an instance with Object.create() and then .apply() with that instance:

var items = Object.create(Items.prototype);
Items.apply(items, [ 1, 2, 3, 4 ]);

console.log(items.sum());

Which when run prints 10 since 1 + 2 + 3 + 4 == 10:

$ node t.js
10

Solution 5

In ES6, Reflect.construct() is quite convenient:

Reflect.construct(F, args)
Share:
81,283
Prem
Author by

Prem

premasagar.com | Dharmafly | Twitter | GitHub

Updated on June 30, 2020

Comments

  • Prem
    Prem almost 4 years

    In JavaScript, I want to create an object instance (via the new operator), but pass an arbitrary number of arguments to the constructor. Is this possible?

    What I want to do is something like this (but the code below does not work):

    function Something(){
        // init stuff
    }
    function createSomething(){
        return new Something.apply(null, arguments);
    }
    var s = createSomething(a,b,c); // 's' is an instance of Something
    

    The Answer

    From the responses here, it became clear that there's no built-in way to call .apply() with the new operator. However, people suggested a number of really interesting solutions to the problem.

    My preferred solution was this one from Matthew Crumley (I've modified it to pass the arguments property):

    var createSomething = (function() {
        function F(args) {
            return Something.apply(this, args);
        }
        F.prototype = Something.prototype;
    
        return function() {
            return new F(arguments);
        }
    })();
    
    • user14128401
      user14128401 over 12 years
      [Matthew Crumley's solution][1] in CoffeeScript: construct = (constructor, args) -> F = -> constructor.apply this,args F.prototype = constructor.prototype new F createSomething = (()-> F = (args) -> Something.apply this.args F.prototype = Something.prototype return -> new Something arguments )() [1]: stackoverflow.com/questions/1606797/…
    • Dmitry Minkovsky
      Dmitry Minkovsky almost 11 years
      I think the takeaway from this thread is that new does two things: (i) sets up the prototype, and (ii) applies that constructor with this set to the said object/prototype combo. You can make that happen either with Object.create(), or by rolling your own take on Object.create() and capturing context with a closure.
    • Killroy
      Killroy about 10 years
      I generalized it by passing the class in as an argument into the outer function. So this is basically a factory-factory.
    • Sergey Kamardin
      Sergey Kamardin almost 10 years
      The answer by @Pumbaa80 is seems better solution, and also used by ES6 Traceur to polyfill spread operator. =) Also it lil bit faster in Chrome: jsperf.com/dynamic-arguments-to-the-constructor
    • through.a.haze
      through.a.haze over 6 years
      Can someone explain me why this guy couldn't do just like that var s = new Something(a,b,c) ? I can't get it :/
    • Alwin Kesler
      Alwin Kesler over 6 years
  • Robert Koritnik
    Robert Koritnik over 14 years
    Using eval is slower and more error prone than using apply() directly.
  • Prem
    Prem over 14 years
    Yes, good idea. I could create an init() method and then use apply() on that. As with my comment on Ionut's approach, it's a bit of a shame that there's not a way to do this without modifying the architecture of the constructor. But this looks like a pragmatic solution.
  • Prem
    Prem over 14 years
    OK, fair enough. This basically restricts the number of args to just one (either an object or an array), but allows an arbitrary number of properties within it.
  • Prem
    Prem over 14 years
    Thanks Matthew. Interesting to call a closure on the fly. Although your example shows the calling function allowing just one argument (an array of args), I guess this could be modified to have it pass on the arguments var instead.
  • Anthony Mills
    Anthony Mills over 14 years
    Yes. Well, it doesn't restrict the number of args at all, really (you just use one of the args as a container for optional arguments), it's just that an object or an array are generally the most useful containers. You'll often see this pattern in constructors; it allows named parameters (good for self-documenting source code) as well as optional parameters.
  • Prem
    Prem over 14 years
    There have been some excellent responses in this thread. I'm going to accept this one as my preferred solution, since it doesn't require modification of the original constructor (I didn't specify that as a requirement in my original question, but I appreciate it nevertheless). So the constructor can be written in any way, and the calling function written independently to add more convenience.
  • Matthew Crumley
    Matthew Crumley almost 13 years
    This is another good way to do it if you have Object.create available.
  • Ansel Halliburton
    Ansel Halliburton over 12 years
    This doesn't work with Date, String or any other function that behaves differently when called as a constructor.
  • Matthew Crumley
    Matthew Crumley over 12 years
    @Pumbaa80 That's a good point. Native constructors know if they're being called as constructors or functions, so they behave differently in this case.
  • Ansel Halliburton
    Ansel Halliburton about 12 years
    You're right in saying that Function.bind can be used instead of Function.prototype.bind, I just left it for clarity. After all, any function could be used: eval.bind would save even more code, but that's really way too confusing.
  • Rob W
    Rob W about 12 years
    @Pumbaa80 My bad, reverted my edit. I tested new (Function.prototype.bind.apply(Array, [1,2,3]));, but forgot that your newCall function already receives a cls argument.
  • Ansel Halliburton
    Ansel Halliburton about 12 years
    @RobW No problem :) I think I'm gonna add a few explanatory words.
  • busticated
    busticated about 12 years
    of course, arguments.callee is depreciated (developer.mozilla.org/en/JavaScript/Reference/…) but you can still just reference your constructor func directly by name
  • Trevor Norris
    Trevor Norris about 12 years
    @busticated: absolutely correct, and I didn't use it in my second snippet.
  • jordancpaul
    jordancpaul over 11 years
    Interesting, but important to remember that IE8 only supports ECMAScript 3
  • John Haugeland
    John Haugeland over 11 years
    I love how this is super downvoted even though it's essentially identical to the top answer.
  • Will Tomlins
    Will Tomlins over 11 years
    The only problem with this solution is that it's going to end up running the constructor twice on the same object with different parameters.
  • Trevor Norris
    Trevor Norris over 11 years
    @WillTomlins: It will end up running the constructor twice, and in a different context. Though I'm not sure how the parameters will have changed. Care to clarify?
  • Will Tomlins
    Will Tomlins over 11 years
    I only meant that the object would be created, then the constructor would be run on that object without any parameters, then the constructor would be run again on the same object with in this case a, b & c. You might find some awkwardness comes out of this when your constructor requires certain parameters.
  • wukong
    wukong over 11 years
    Hi, Matthew, It's better to fix the constructor property aslo. An enhanced version of your answer. stackoverflow.com/a/13931627/897889
  • Jean Vincent
    Jean Vincent over 11 years
    I think you are right fixing the constructor is useful unless the application never checks it which cannot be implied when writing a library.
  • jasonkarns
    jasonkarns almost 11 years
    This doesn't address the OP's question. Notice when you create vars s and z the number of args passed to Something is static.
  • AnnanFay
    AnnanFay over 10 years
    This method doesn't work with some libraries which have their own weird inheritance code. For example with KineticJS.
  • hashchange
    hashchange over 10 years
    I have had a look at it in a debugger (evaluating newObject.constructor == ConstructorFunction at a breakpoint). Turns out that the assignment isn't needed at all, it is set correctly in the code of @MatthewCrumley.
  • Adaptabi
    Adaptabi about 10 years
    I don't understand why you need a new closure function every time when you instantiate a new class? That is crap, performance-wise
  • Royi Namir
    Royi Namir about 10 years
    I found your solution very nice. but it is confusing. cuz in the first sample you wrote newCall(Something, a, b, c); where a would be the context for the bind but in the second example of yours you did mention did context is pointless - so you send null. for me it was very confusing ( would you believe me that I thought about it for 3 days?) - anyway your first code (to be persistent to your second example) needs to be : function newCall(Cls) {var arr=Array.prototype.slice.call(arguments);arr.shift(); return new (Function.prototype.bind.apply(Cls, [null,arr]));
  • Royi Namir
    Royi Namir about 10 years
    small fix : instead of [null,arr] - [null].concat(arr)
  • Ansel Halliburton
    Ansel Halliburton about 10 years
    @RoyiNamir Sure, it could be done that way, but in the end you have basically just replaced the first item of arr by null. And again, it could be anything. Why is null better than Something? Oh and don't worry: this is indeed a very complicated topic. It took me quite a while to figure it out, so 3 days is totally fine I guess
  • Royi Namir
    Royi Namir about 10 years
    +1 , :-) again just for consistency (first example vs second example). so beginners wont be confused. :-). you can just add remark.
  • Wilt
    Wilt about 10 years
    You did manage where most people failed: the object is of the correct type and I can later use instanceof etc. Thanks!
  • zamnuts
    zamnuts about 10 years
    jsfiddle with some tricks from the comments, and then some: jsfiddle.net/82aJR/1
  • goat
    goat about 10 years
    One drawback is that the resulting constructed object myObj.constructor.name property will be set to F for everything. This kinda sucks when looking at stack traces and drilldown dumps inside of debuggers, because they use that name everywhere.
  • advncd
    advncd about 10 years
    You answer is great. However, I guess I have solved this with a simpler approach. Please take a look at my answer below and let me know what you think.
  • advncd
    advncd about 10 years
    I didn't see your answer and tried to re-invent the 'new' keyword :) See my answer below.
  • Sergey Kamardin
    Sergey Kamardin almost 10 years
    Vote for your answer cause its pretty good! Was used in my projects, and now wrote jsperf for it jsperf.com/dynamic-arguments-to-the-constructor =)
  • Michael Chernetsov
    Michael Chernetsov over 9 years
    Warning: The proto property is deprecated and should not be used. Object.getPrototypeOf should be used instead of the proto getter to determine the [[Prototype]] of an object. Mutating the [[Prototype]] of an object, no matter how this is accomplished, is strongly discouraged, because it is very slow and unavoidably slows down subsequent execution in modern JavaScript implementations. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • advncd
    advncd over 9 years
    You are right about the proto object. That's why I have used Object.setPrototypeOf. And the whole thing is just a "simulation" of 'new' keyword. It clearly shows how JavaScript's new keyword works and how it actually instantiates the objects.
  • jjrv
    jjrv over 8 years
    The accepted answer does not work, but this one does.
  • bobef
    bobef over 8 years
    Bonus: this works with ES6 classes (tested in Node.js 4.0.0). With other methods you get an error that you can not call the constructor without new, but this works. Thanks.
  • jordancpaul
    jordancpaul about 8 years
    @DotNetWise I agree, no need for a closure function. Check out my answer below - it performs better and works with TypeScript as well since it doesn't require the constructor to return a value.
  • Wilt
    Wilt almost 8 years
    Can you explain why the length of my first argument in this case is not working correctly? It is very strange.
  • Wilt
    Wilt almost 8 years
    What is the difference to what @user123444555621 proposed? Can you add some explanation to the differences in your answer?
  • Wilt
    Wilt almost 8 years
    This is a really nice solution. Also works for this case where the solution of @user123444555621 fails.
  • jfriend00
    jfriend00 almost 8 years
    For logging reasons, I'm trying to "wrap" a constructor and I am trying to use this answer to be able to pass an arbitrary number of arguments to the real constructor. But, this does not work for me and the reason it does not is that Cls.bind.apply(Cls, arguments); returns a new function that does not have the prototype that Cls does so I get a new object, but without the correct prototype. The Matthew Crumley answer below seems to address the prototype issue.
  • jsdw
    jsdw over 7 years
    This isn't completely identical, since arguments.length will always be 2 inside your Person function, whereas instead using new (Function.prototype.bind.apply(Person, arguments)) in person will set arguments to the correct value inside Person as well.
  • trss
    trss over 7 years
    The last note at the section "Using apply to chain constructors" mentions this same approach but adds anyhow this is not the best way to do things and probably should not be used in any production environment. I'm guessing that the prototype issue when wrapping is a reason. Are there any other reasons?
  • trss
    trss over 7 years
    @Wilt: The JSFiddle must have [null].concat(args) instead of just args in the bind.apply. Check out the fixed version.
  • trss
    trss over 7 years
    The creation of a copy of the entire array of arguments looks like a stronger reason.
  • trickpatty
    trickpatty over 7 years
    I've been trying to understand how to use Reflect.construct to apply this to the new class I am constructing "F", but it seems like the context of this inside F is still new, and the old properties of this do not carry over, has anyone come across any good blog posts or examples of how to use Reflect.construct() ?
  • Bugs
    Bugs about 7 years
    Links to external resources are encouraged, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline.
  • Thomas Wagenaar
    Thomas Wagenaar about 7 years
    best answer here!
  • Manngo
    Manngo almost 7 years
    This solved a problem of mine in creating a “new-less safe” constructor. It goes a little like this: function Thing() { if(!(this instanceof Thing)) { var data=[null].concat(Array.prototype.slice.call(arguments)); return new (Thing.bind.apply(Thing,data)); } // regular constructor }.
  • Pian0_M4n
    Pian0_M4n almost 7 years
    Or the simpler version: function(...args) { return new myClass(...args); }
  • Martin Wantke
    Martin Wantke over 6 years
    You can also use bind like this: let constr = A.bind(null, 'a', 'b', 'c'); let obj = new constr(); If the first parameter of bind, apply or call is empty, the this pointer inside the function reference always to window.
  • Jelle De Loecker
    Jelle De Loecker over 6 years
    All the other answers were good back in the day, but this method is the clear winner now. In the future Reflect.construct might be better, though
  • Simon D
    Simon D almost 6 years
    @trss what is that additional null in the array doing?
  • trss
    trss almost 6 years
  • Henok Tesfaye
    Henok Tesfaye almost 6 years
    Using Spread syntax,we can pass variable number of arguments to the constructor. Passing an iterable types like array as an argument and finally the array will expand to each parameters in the constructor.
  • unsynchronized
    unsynchronized over 4 years
    to go full meta: function func(args,code){return newCall.apply(this,[Function].concat(args,[code]));} and func (["a","b"],"return a+b")(1,2) ---> 3
  • Rustam
    Rustam over 3 years
    Keep in mind this won't work in Chrome 49. But answer with spread operator works in Chrome 49
  • Teemu
    Teemu over 2 years
    It's notable, that using new operator with a dynamic constructor is not useful, you can use a regular object factory instead.