I'm just getting into Backbone, and one thing that I don't understand is why the 'on()' method for models always takes three arguments--event, handler, and context.
It seems that almost always 'this' is used for context and I haven't seen any other usage. Even if there were, since I haven't seen one yet it must be pretty rare.
So my question is: When does one use a context other than 'this', and why is Backbone designed this way? By the way, I do understand why you need to provide context, it's just that I wonder why the method syntax specifies that I use three arguments instead of making the last argument optional--which seems to be always 'this' and feels redundant. I'm sure I'm missing something. Please someone help me understand. Thank you!
[EDIT] Why can't one do something like:
model.on = function(event, callback){
model.on_with_three_args.call(this, event, callback, this);
});
model.on_with_three_args = function(event, callback){
/* whatever the on() is supposed to do */
});
Suppose we're in a view that's based on a model and we want to bind to the model's change event:
this.model.on('change', this.render);
The on
call sees two things:
- The event name, a simple string.
- The handler, a function.
on
has no way of knowing what this
means in this.render
, it just sees a function; on
won't even know the difference between the call above and this:
this.model.on('change', function() { ... });
If your function needs a particular context then you have two choices:
- Create a bound function using
_.bind
, _.bindAll
, Function.bind
, $.proxy
, CoffeeScripts =>
, the var _this = this
closure trick, or any of the ways of creating or simulating a bound function.
Tell it which context you want by saying:
this.model.on('change', this.render, this);
There's no way to unroll the call stack to see which this
you want so you have to be explicit about it.
Backbone will call the callback like this:
node.callback.apply(node.context || this, ...);
where node.callback
is the callback function and node.context
is the third argument (if any) given to on
. If you don't specify the context then you'll get whatever this
happens to be when trigger
is called; in the example above, this
would end up being the model.
So the third argument to on
actually is optional but the default value isn't terribly useful and there is no way to choose a better default, the information you need to choose a sensible context simply isn't accessible in JavaScript. This is why you see so much _.bindAll(this, ...)
boilerplate in Backbone views.
If you tried something like this:
model.on = function(event, callback){
model.on_with_three_args.call(this, event, callback, this);
});
then this
in that context would usually be model
so you'd really be saying:
model.on = function(event, callback){
model.on_with_three_args.call(model, event, callback, model);
});
or
model.on = function(event, callback){
model.on_with_three_args(event, callback, model);
});
and there's little point to any of that. The value of this
inside on
has little if anything to do with the value of this
in the code that calls on
. this
in JavaScript is not a variable, it is a keyword which refers to the current calling context.