Muestra "Todos" de Backbone.js: no estoy seguro de por qué funciona cierto fragmento de código

This question is about the "Todos" Backbone.js sample, which is at: http://documentcloud.github.com/backbone/docs/todos.html

The following block of code es en "La aplicación" section, and iterates through the Todos collection. My issue is that the addOne function is passed as a reference to the Todos collection, but that function has a reference to this, which is not the same as what this would refer to when the function is called by the Todos collection object.

addOne: function(todo) {
    var view = new TodoView({model: todo});
    this.$("#todo-list").append(view.render().el);
},
addAll: function() {
    Todos.each(this.addOne);
},

Why does the function execute correctly when the caller is not calling it in the context of the instantiated AppView objeto?

preguntado el 08 de noviembre de 11 a las 19:11

4 Respuestas

I just worked it out. It occurred to me that this se refiere a window object by default and seeing as jQuery registers $ globally, the function will work even if called with no context object.

respondido 08 nov., 11:23

ya--you've stumbled on something that is almost "working by accident". They probably should have had a _(this).bind("addOne") statement in the initialize to ensure that the this pointer was the view and not the window - timDunham

Not exactly. Take a look at my answer, the trick is bind’s third argument. - s4y

i thought that at first too, but check Nathan's reply to your answer - timDunham

Confirmed with a breakpoint. Got a good chuckle out of this one. +1 for a good find! - Brian Nickel

Ahh, good catch! Sounds like it's time to submit an bug report to Backbone. - s4y

"If jQuery or Zepto is included on the page, each view has a $ function that runs queries scoped within the view's element."

http://documentcloud.github.com/backbone/#View-dollar

Context is the same (view) for both addOne and addAll and it is achieved with the third parameter in bind calls.

Todos.bind('add',   this.addOne, this);
Todos.bind('reset', this.addAll, this);

http://documentcloud.github.com/backbone/#Events-bind

--editar

Hmm.. then again do those binds ensure the context when addOne is ran with each?

addAll: function() {
  Todos.each(this.addOne);
},

respondido 09 nov., 11:00

Yeah I saw that views have a $ property, but the code was being executed in the context of the collection, which as far as I can tell does not have a $ property. - Nathan Ridley

Heh, true. I just checked the example and this refers indeed to window when ran with each. - Heikki

For future Googlers, this issue was Subido on GitHub and a fijar was applied, but it doesn't really address the issue.

I think the best way to solve this problem is have the addAll function bind its own this de las personas acusadas injustamente llamadas .each iteration, i.e.

addOne: function(todo) {
  var view = new TodoView({model: todo});
  this.$("#todo-list").append(view.render().el);
},

addAll: function() {
  Todos.each(this.addOne, this); // notice the second parameter
},

We can do this because Underscore.js's _.each function has an option third (second, in our case) parameter to specify the context for the iteration.

contestado el 08 de mayo de 12 a las 10:05

Echa un vistazo a la initialize método:

initialize: function() {
  this.input    = this.$("#new-todo");

  Todos.bind('add',   this.addOne, this);
  Todos.bind('reset', this.addAll, this);
  Todos.bind('all',   this.render, this);

  Todos.fetch();
}

That third (optional) argument to bind is the context with which the callback gets called. It's documented aquí. The implementation in Backbone is aquí:

callback[0].apply(callback[1] || this, args);

respondido 08 nov., 11:23

Yes, but the line Todos.each(this.addOne); calls the collection's each function directly. How does Todos know to call the passed function reference with the view as the context? - Nathan Ridley

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.