Frankly I don't give a twopenny toss about good or bad practices.
If your aim is to understand anything at all, you'd better forget about them.
This being said, to understand when it is appropriate to use call
/apply
or bind
, what you have to understand is closures, and the specific closure of this
.
I use a broad definition of closure here. Not the one you will usually get in JavaScript technical chats, where we usually talk about lexical closures.
For the purpose of this little post, let's say a closure will be anything that provides a value for any variable (this
included) that lives outside the current scope of the function you're focusing on.
bind
, call
and apply
are basically there to supply a value for this
(and some other arguments as an option).
It is only useful for two things:
- invoke functions or methods of a given class to an object of a different class (or no class at all).
more on that later
- supply a value for
this
in case the current closure does not suit your needs.
For instance, passing a reference to a method loses the connection to the underlying object instance. Or using a class prototype function. Or being called in the context of an event handler where JS has set this to the DOM element that caught the event.
call & apply
call
will simply set this
to the value of its first argument for this particular execution of the function.
Since creating objects is so easy in JS, you might want to do something like:
Worker = function ()
{
this.things_done = 0;
}
Worker.prototype = {
do_something: function (count)
{
this.things_done += count;
}
}
var worker= new Worker(); // Worker object
var wanabee_worker = { things_done: 100 }; // classless object
Et voilà! You've just created something that has no class relation with Worker
but still can use Worker
methods, since it has all the required properties defined.
worker.do_something.call (wanabee_worker, 10);
allows wanabee_worker
to borrow unrelated object Worker
's methods.
The opposite can also be used:
function reset_work_count ()
{
this.things_done = 0;
}
reset_work_count.call (worker);
Here we have a plain function that does not have anything to do with Worker
, except it uses the same properties. call
allows to apply it to a Worker
object.
apply
does exactly the same as far as this
is concerned. The only difference is the way other arguments are passed.
bind
bind
will create an internal closure and return a new wrapper function that will use the parameter passed to bind
as a value for this
.
Typical example: bind an event handler to a specific object.
$('#binder').click(person.fullName.bind(person));
Beneath the JQuery goo, what the code does eventually is
binder.addEventListener ('click', person.fullName.bind(person), false);
If the handler was simply defined as person.fullName
, it would be called with this
set to the DOM element that caught the event.
In that particular case, the closure of this
provided by the JS engine does not suit our needs, so we provide an alternative one using bind
.
Instead of person.fullName.bind(person)
, you could have used:
function() { person.FullName(); }
except that
- the lambda function is cumbersome and obfucates the code,
bind
is an internal construct that does the closure a bit more efficiently.
You could also imagine that the object used to handle the event will be dynamically allocated/computed by some proxy function. In that case, using bind
would be useless since what we want is access to the methods that will allow us to use a lambda object as one of a working class.
function event_handler (param)
{
var obj = compute_lambda_obj ();
Worker.prototype.do_something.call (ojb, param);
}