Reputation: 11095
var PlaylistView = Backbone.View.extend({
el: '#expanded-container',
initialize: function() {
this.bg = chrome.extension.getBackgroundPage();
this.$('.list-group').empty();
var realThis = this;
_.each(this.bg.Playlist.models, function (song) {
// append to playlist, rendering song template?
var songView = new SongView({ model: song });
console.log(realThis); // THIS is what I want
console.log(this) // this is NOT what I want
//this.$el.append(songView.render().el); // hence, this does NOT work
realThis.$el.append(songView.render().el); // and THIS works
});
}
});
In the above code, this
inside _.each()
function points at the global window
object because _.each()
is invoked by the window. However, I still want this
to point at PlaylistView
. I have faced many similar situations and I often defined a variable that stored the initial value of this, just like realThis
variable in the provided example. Is there any other conventional way to deal with this?
Note: I am following this book to learn Backbone, and it shows the following code as example.
var ListView = Backbone.View.extend({
render: function(){
// Assume our model exposes the items we will
// display in our list
var items = this.model.get('items');
// Loop through each of our items using the Underscore
// _.each iterator
_.each(items, function(item){
// Create a new instance of the ItemView, passing
// it a specific model item
var itemView = new ItemView({ model: item });
// The itemView's DOM element is appended after it
// has been rendered. Here, the 'return this' is helpful
// as the itemView renders its model. Later, we ask for
// its output ("el")
this.$el.append( itemView.render().el ); // <--- *THIS IS WRONG?
}, this);
}
});
In this case, wouldn't this
inside _.each
loop point at the wrong object, just like in my code? Is this an error in the book or am I misunderstanding something? Thank you!
Reference: Learning this keyword
Upvotes: 0
Views: 397
Reputation: 9466
From http://underscorejs.org/#each:
each
_.each(list, iterator, [context])
The iterator is bound to the context object, if one is passed.
Within initialize()
, this
points to your Backbone View. If you pass this
as the 3rd argument to _.each()
then this
will refer to your Backbone View within the iterator function.
I have faced many similar situations and I often defined a variable that stored the initial value of this, just like realThis variable in the provided example. Is there any other conventional way to deal with this?
Yes. If you are in an ES5 (non-IE8) environment, use Function.prototype.bind()
. For backwards compatibility, use _.bind()
.
var func = function (greeting) {
return greeting + ': ' + this.name;
};
if (usingUnderscore) {
func = _.bind(func, {name: 'moe'}, 'hi');
} else if (es5environmentHooray) {
func = func.bind({name: 'moe'}, 'hi');
}
func();
=> 'hi: moe'
Upvotes: 2
Reputation: 20518
You can change this
of a particular function by using .bind()
:
function foo() {
alert(this.x);
}
var boundFoo = foo.bind({x: "bar"});
boundFoo();
This alerts "bar".
Saving the outer this
as realThis
or that
is also common if you want access to both the inner and outer this
.
Underscore uses .call()
to change this
of the iterator function you pass it. Specifically, _.each()
has a third parameter that allows you to specify what you want this
to be, so that example is correct. See the underscore.js source.
Upvotes: 1