I am learning backbone.js, and feel confused on this:
I am following the tutorial :
http://arturadib.com/hello-backbonejs/
as you can see in the first example (1.js):
(function($){
var ListView = Backbone.View.extend({
el: $('body'), // attaches `this.el` to an existing element.
initialize: function(){
_.bindAll(this, 'render'); // fixes loss of context for 'this' within methods
this.render(); // not all views are self-rendering. This one is.
},
render: function(){
$(this.el).append("<ul> <li>hello world</li> </ul>");
}
});
var listView = new ListView();
})(jQuery);
But if I comment out the sentence: _.bindAll(this, 'render');
, this will still work. I have searched in google and someone said that the method bindAll()
is necessary since if I switched my context, the calling of this.render
may unavailable. I feel confused on the "context". and also could some one explain me when the calling (this.render
) will unavailable?
For the example you've given _.bindAll(this, 'render');
isn't necessary but if you have callback functions where this
can possibly be changed to the context of something else, then _bindAll()
can be handy.
For instance:
initialize: function(){
_.bindAll(this, 'render', 'clickFunc');
},
events: {
'click .someElement': 'clickFunc'
},
clickFunc: function(e) {
/** If you remove the clickFunc from the list of events in bindAll,
'this' will refer to the element that invoked the event.
Adding the clickFunc event in the _.bindAll, ensures that 'this' stays
as the view.
*/
this /** <-- our focal point */
}
- Any methods listed as property values in your view's events hash are automatically bound for you by backbone
- Any methods in your view that you manually use as event handlers from models or collections should be manually bound via
bindAll
- OR you can provide a context when you register the binding
- OR you can use EMCA 5's function.bind to get the same result
snippet:
events: {
'click .win': 'win',
'click .lose': 'lose'
},
initialize: function () {
//win and lose are automatically bound for you
//because they are in the events property
//refresh must be manually bound
this.model.on('change', this.refresh);
//which you can do ECMA5 style if you like
this.model.on('change', this.refresh.bind(this));
//OR you can provide a context backbone style
this.model.on('change:foo', this.fooChange, this);
//However, since you pretty much never want an unbound function
//in a view, you can just stick this in all your initialize methods
//and call it done
//Note this will bind all functions in your view class if you don't
//pass specific method names. I recommend this form.
_.bindAll(this);
},
win: function () {...},
lose: function () {...},
refresh: function () {...},
fooChange: function() {...}
...OOOOORRRR just use CoffeeScript and fat arrows and solve this cleanly at the language level.
in this instance you don't need the _.bindAll
, but let's say your view have a method that causes a rerender and you do something like this:
..,
myMethod: function() {
this.$('.someselector').change(this.render);
},
if you don't have _.bindAll
for render
, your context would be off.
Lets take a close look at what _.bindAll
does from underscore.js offical docs.
_.bindAll = function(obj) {
var i, length = arguments.length, key;
if (length <= 1) throw new Error('bindAll must be passed function names');
for (i = 1; i < length; i++) {
key = arguments[i];
obj[key] = _.bind(obj[key], obj);
}
return obj;
};
What it does is to automatically bind all its functions to its correct context. (where its function is declared rather than invoked.
I personally think it was a convention for old version of Backbone.js to bind its events
, or DOM action listeners. Since new versions Backbone View
automatically bind and unbind listeners in events
. Find more by search Binding "this"
here
I hope it helps.