It's usually best to use listenTo()
From Backbone Essentials by Addy Osmani:
While on()
and off()
add callbacks directly to an observed object,
listenTo()
tells an object to listen for events on another object,
allowing the listener to keep track of the events for which it is
listening. stopListening()
can subsequently be called on the listener
to tell it to stop listening for events:
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
var c = _.extend({}, Backbone.Events);
// add listeners to A for events on B and C
a.listenTo(b, 'anything', function(event){ console.log("anything happened"); });
a.listenTo(c, 'everything', function(event){ console.log("everything happened"); });
// trigger an event
b.trigger('anything'); // logs: anything happened
// stop listening
a.stopListening();
// A does not receive these events
b.trigger('anything');
c.trigger('everything');
If you use on and off and remove views and their corresponding models
at the same time, there are generally no problems. But a problem
arises when you remove a view that had registered to be notified about
events on a model, but you don’t remove the model or call off to
remove the view’s event handler. Since the model has a reference to
the view’s callback function, the JavaScript garbage collector cannot
remove the view from memory. This is called a ghost view and is a form
of memory leak which is common since the models generally tend to
outlive the corresponding views during an application’s lifecycle. For
details on the topic and a solution, check this excellent article by
Derick Bailey.
Practically, every on
called on an object also requires an off
to be
called in order for the garbage collector to do its job. listenTo()
changes that, allowing Views to bind to Model notifications and unbind
from all of them with just one call - stopListening()
.
The default implementation of View.remove()
makes a call to
stopListening()
, ensuring that any listeners bound using listenTo()
are unbound before the view is destroyed.
var view = new Backbone.View();
var b = _.extend({}, Backbone.Events);
view.listenTo(b, 'all', function(){ console.log(true); });
b.trigger('anything'); // logs: true
view.listenTo(b, 'all', function(){ console.log(false); });
view.remove(); // stopListening() implicitly called
b.trigger('anything'); // does not log anything