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);
}
})();
With ECMAScript5's
Function.prototype.bind
things get pretty clean:It can be used as follows:
or even directly:
This and the eval-based solution are the only ones that always work, even with special constructors like
Date
:edit
A bit of explanation: We need to run
new
on a function that takes a limited number of arguments. Thebind
method allows us to do it like so:The
anything
parameter doesn't matter much, since thenew
keyword resetsf
's context. However, it is required for syntactical reasons. Now, for thebind
call: We need to pass a variable number of arguments, so this does the trick:Let's wrap that in a function.
Cls
is passed as arugment 0, so it's gonna be ouranything
.Actually, the temporary
f
variable is not needed at all:Finally, we should make sure that
bind
is really what we need. (Cls.bind
may have been overwritten). So replace it byFunction.prototype.bind
, and we get the final result as above.Thanks to posts here I've used it this way:
and implementation:
Any function (even a constructor) can take a variable number of arguments. Each function has an "arguments" variable which can be cast to an array with
[].slice.call(arguments)
.The above tests produce the following output:
Here is my version of
createSomething
:Based on that, I tried to simulate the
new
keyword of JavaScript:I tested it and it seems that it works perfectly fine for all scenarios. It also works on native constructors like
Date
. Here are some tests:While the other approaches are workable, they're unduly complex. In Clojure you generally create a function that instantiates types/records and use that function as the mechanism for instantiation. Translating this to JavaScript:
By taking this approach you avoid the use of
new
except as described above. And this function, of course, has no issues working withapply
or any number of other functional programming features.By using this approach, all of your type constructors (e.g.
Person
) are vanilla, do-nothing constructors. You just pass in arguments and assign them to properties of the same name. The hairy details go in the constructor function (e.g.person
).It is of little bother having to create these extra constructor functions since they are a good practice anyhow. They can be convenient since they allow you to potentially have several constructor functions with different nuances.