I was looking for solutions for calling Javascript constructors with an arbitrary number of arguments, and found some good SO posts, which led me to believe that these three
The previously accepted answer was incorrect. You can use bind, call and apply with constructors to create new constructors just fine -- the only problem in your test is that you've forgotten that bind.apply and bind.call are applying and calling bind, not the constructor itself, so you gave the wrong arguments.
f = Date.bind(null, 2000,0,1)
g = Function.bind.call(Date, null, 2000, 0, 1)
h = Function.bind.apply(Date, [ null, 2000, 0, 1 ])
new f() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new g() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new h() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
All three are instanceof
Date.
Call's arguments are the execution context followed by the arguments to apply. Apply's arguments are the execution context and an array of arguments. Bind's arguments are the execution context followed by the arguments to bind.
So the arguments to apply, for example, are the context for which to apply bind (Date) followed by an array which is the arguments for bind (so the first array member is the bind's context argument). This is why it's confusing to call or apply bind; it feels strange to supply context arguments to both.
Note that, when using bind with constructors, the context argument is always ignored because 'new' explicitly creates a new context. I use null when the context argument is irrelevant to keep that clear, but it can be anything.
Meanwhile, apply and call in these examples do need to know that the context in which they are to apply/call bind is the Date function. I switched 'Date' to 'Function' where possible to help illuminate what is actually supplying context where. When we call apply or call on Date.bind, we are really calling apply or call on the bind method unattached to the Date object. The bind method in such a case could come from any function at all. It could be Number.bind.call(Date, null, 2000, 0, 1) and the result would be exactly the same.
If it's not obvious why, consider the difference between the following examples:
context.method();
and
var noLongerAMethod = context.method;
noLongerAMethod();
In the second case, the method has been divorced from its original context (...unless it was previously bound) and will behave differently if it was relying on 'this' internally. When we pull bind off any given function as a property, rather than executing it directly, it is simply another pointer to the generic bind method on Function.prototype.
Personally I don't think I've ever needed to call or apply bind, and it's hard to imagine a situation for which it would be a good solution, but binding constructors to create new constructors is something I've found very useful on occasion. In any case it's a fun puzzle.
bind
and apply
/ call
only works work invocation to function but not constructor, so basically with native methods you cannot do this, one way is to write a bindConstruct
method but it may involves extra complexity:
function bindConstruct(fn) {
// since constructor always accepts a static this value
// so bindConstruct cannot specify this
var extraArgs = [].slice.call(arguments, 1);
// create a 'subclass' of fn
function sub() {
var args = extraArgs.concat([].slice.call(arguments));
fn.apply(this, args);
}
sub.prototype = fn.prototype;
sub.prototype.constructor = sub;
return sub;
}
This, actually, creates a subclass to your constructor.
Then your code:
var MyClass = function(x, y) {
console.log(arguments);
console.log(x + y);
}
var BindedMyClass = bindConstruct(MyClass, 1, 2, 3);
var c = new BindedMyClass(4, 5);
console.log(c instanceof MyClass);
console.log(c instanceof BindedMyClass);
You may also write this function to Function.prototype
or as an extension to the native bind
function.