Use of .apply() with 'new' operator. Is this possible?
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)
Comments
-
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 thenew
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 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 almost 11 yearsI think the takeaway from this thread is that
new
does two things: (i) sets up the prototype, and (ii) applies that constructor withthis
set to the said object/prototype combo. You can make that happen either withObject.create()
, or by rolling your own take onObject.create()
and capturing context with a closure. -
Killroy about 10 yearsI generalized it by passing the class in as an argument into the outer function. So this is basically a factory-factory.
-
Sergey Kamardin almost 10 yearsThe 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 over 6 yearsCan 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 over 6 yearsA little update from MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
-
-
Robert Koritnik over 14 yearsUsing eval is slower and more error prone than using apply() directly.
-
Prem over 14 yearsYes, good idea. I could create an
init()
method and then useapply()
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 over 14 yearsOK, 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 over 14 yearsThanks 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 over 14 yearsYes. 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 over 14 yearsThere 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 almost 13 yearsThis is another good way to do it if you have
Object.create
available. -
Ansel Halliburton over 12 yearsThis doesn't work with
Date
,String
or any other function that behaves differently when called as a constructor. -
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 about 12 yearsYou're right in saying that
Function.bind
can be used instead ofFunction.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 about 12 years@Pumbaa80 My bad, reverted my edit. I tested
new (Function.prototype.bind.apply(Array, [1,2,3]));
, but forgot that yournewCall
function already receives acls
argument. -
Ansel Halliburton about 12 years@RobW No problem :) I think I'm gonna add a few explanatory words.
-
busticated about 12 yearsof 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 about 12 years@busticated: absolutely correct, and I didn't use it in my second snippet.
-
jordancpaul over 11 yearsInteresting, but important to remember that IE8 only supports ECMAScript 3
-
John Haugeland over 11 yearsI love how this is super downvoted even though it's essentially identical to the top answer.
-
Will Tomlins over 11 yearsThe 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 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 over 11 yearsI 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 over 11 yearsHi, Matthew, It's better to fix the constructor property aslo. An enhanced version of your answer. stackoverflow.com/a/13931627/897889
-
Jean Vincent over 11 yearsI think you are right fixing the constructor is useful unless the application never checks it which cannot be implied when writing a library.
-
jasonkarns almost 11 yearsThis doesn't address the OP's question. Notice when you create vars
s
andz
the number of args passed toSomething
is static. -
AnnanFay over 10 yearsThis method doesn't work with some libraries which have their own weird inheritance code. For example with KineticJS.
-
hashchange over 10 yearsI 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 about 10 yearsI 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 about 10 yearsI found your solution very nice. but it is confusing. cuz in the first sample you wrote
newCall(Something, a, b, c);
wherea
would be the context for the bind but in the second example of yours you did mention did context is pointless - so you sendnull
. 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 about 10 yearssmall fix : instead of
[null,arr]
-[null].concat(arr)
-
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
bynull
. And again, it could be anything. Why isnull
better thanSomething
? 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 about 10 years+1 , :-) again just for consistency (first example vs second example). so beginners wont be confused. :-). you can just add remark.
-
Wilt about 10 yearsYou did manage where most people failed: the object is of the correct type and I can later use
instanceof
etc. Thanks! -
zamnuts about 10 yearsjsfiddle with some tricks from the comments, and then some: jsfiddle.net/82aJR/1
-
goat about 10 yearsOne drawback is that the resulting constructed object
myObj.constructor.name
property will be set toF
for everything. This kinda sucks when looking at stack traces and drilldown dumps inside of debuggers, because they use that name everywhere. -
advncd about 10 yearsYou 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 about 10 yearsI didn't see your answer and tried to re-invent the 'new' keyword :) See my answer below.
-
Sergey Kamardin almost 10 yearsVote 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 over 9 yearsWarning: 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 over 9 yearsYou 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 over 8 yearsThe accepted answer does not work, but this one does.
-
bobef over 8 yearsBonus: 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 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 almost 8 yearsCan you explain why the length of my first argument in this case is not working correctly? It is very strange.
-
Wilt almost 8 yearsWhat is the difference to what @user123444555621 proposed? Can you add some explanation to the differences in your answer?
-
Wilt almost 8 yearsThis is a really nice solution. Also works for this case where the solution of @user123444555621 fails.
-
jfriend00 almost 8 yearsFor 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 thatCls
does so I get a new object, but without the correct prototype. The Matthew Crumley answer below seems to address the prototype issue. -
jsdw over 7 yearsThis isn't completely identical, since
arguments.length
will always be 2 inside yourPerson
function, whereas instead usingnew (Function.prototype.bind.apply(Person, arguments))
inperson
will set arguments to the correct value insidePerson
as well. -
trss over 7 yearsThe 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 over 7 years@Wilt: The JSFiddle must have
[null].concat(args)
instead of justargs
in thebind.apply
. Check out the fixed version. -
trss over 7 yearsThe creation of a copy of the entire array of arguments looks like a stronger reason.
-
trickpatty over 7 yearsI'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 ofthis
inside F is still new, and the old properties ofthis
do not carry over, has anyone come across any good blog posts or examples of how to useReflect.construct()
? -
Bugs about 7 yearsLinks 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 about 7 yearsbest answer here!
-
Manngo almost 7 yearsThis 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 almost 7 yearsOr the simpler version:
function(...args) { return new myClass(...args); }
-
Martin Wantke over 6 yearsYou 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 over 6 yearsAll 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 almost 6 years@trss what is that additional null in the array doing?
-
trss almost 6 yearsIt's the
thisArg
mentioned at developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. -
Henok Tesfaye almost 6 yearsUsing 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 over 4 yearsto go full meta:
function func(args,code){return newCall.apply(this,[Function].concat(args,[code]));}
andfunc (["a","b"],"return a+b")(1,2)
---> 3 -
Rustam over 3 yearsKeep in mind this won't work in Chrome 49. But answer with spread operator works in Chrome 49
-
Teemu over 2 yearsIt's notable, that using new operator with a dynamic constructor is not useful, you can use a regular object factory instead.