Why does Backbone.js model's 'on()' take 'this' as last parameter if it's almost always going to be this?

backbone-events, backbone.js, javascript, this

Solution

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.

Problem

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 */ }); ```

Original source