Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

binding/applying constructors in JavaScript

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 calls should work the same. However, at least in rhino and node.js, they do not:

1. f = Date.bind(Date, 2000,0,1)
2. g = Date.bind.call(Date, 2000, 0, 1)
3. h = Date.bind.apply(Date, [2000, 0, 1])

The first one has the desired result:

print(new f()) //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)

But the other two don't:

print(new g()) //=> Thu Feb 01 1900 00:00:00 GMT-0500 (EST)
print(new h()) //=> Wed Jun 01 1904 00:00:00 GMT-0400 (EST)

So something's gone haywire somewhere. Thoughts on what? Is it just a bad idea to mix things like apply, bind, and/or call with new?

like image 738
Mark Reed Avatar asked May 15 '12 06:05

Mark Reed


People also ask

What is binding in JavaScript?

bind() The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

What is apply and bind function in JavaScript?

Apply is very similar to the call function. The only difference is that in apply you can pass an array as an argument list. Bind is a function that helps you create another function that you can execute later with the new context of this that is provided.

What is the difference between call () and apply () methods?

The Difference Between call() and apply() The difference is: The call() method takes arguments separately. The apply() method takes arguments as an array. The apply() method is very handy if you want to use an array instead of an argument list.

What are constructors used for in JavaScript?

A constructor is a special function that creates and initializes an object instance of a class. In JavaScript, a constructor gets called when an object is created using the new keyword. The purpose of a constructor is to create a new object and set values for any existing object properties.


1 Answers

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.

like image 110
Semicolon Avatar answered Oct 01 '22 06:10

Semicolon