考虑代码(also on JSFiddle):
// Backbone calls this "view3"
InnerView = Backbone.View.extend({
initialize: function() {
this.parent = this.options.parent;
this.listenTo(
this.model,
"change:name",
function() { this.parent.onNameChange() }
);
}
});
// Backbone calls this "view2"
OuterView = Backbone.View.extend({
initialize: function() {
this.innerView = new InnerView({
model: this.model,
parent: this
});
},
onNameChange: function() {
console.log("'this' is bound to the outerview instance: " + (this.cid === "view2"));
}
});
var myModel = new Backbone.Model({ name: "foo" });
var outerView = new OuterView({ model: myModel });
// trigger onNameChange
myModel.set("name", "bar");
这会将'this' is bound to the outerview instance: true
打印到控制台。但是,如果我将回调更改为:
this.listenTo(
this.model,
"change:name",
this.parent.onNameChange
);
(我在this fiddle完成),然后我的控制台显示'this' is bound to the outerview instance: false
。似乎this
已绑定到InnerView
实例。
这是为什么?在阅读listenTo docs后,我希望this
始终绑定到InnerView
个实例,因为listenTo
内部会调用InnerView
。
答案 0 :(得分:2)
这是正确的行为。在第一个例子中,这指的是“this.parent”,其中第二个例子引用“this”。
第一个例子
this.listenTo(
this.model,
"change:name",
function() { this.parent.onNameChange() }
);
onNameChange: function() {
// here this refers to "this.parent"
}
第二个例子
this.listenTo(
this.model,
"change:name",
this.parent.onNameChange // Reference to onNameChange detached from "parent"
);
onNameChange: function() {
// here this refers to "this" and not "this.parent"
}
<强>解决方案强>
如果您想使用第二个示例中的代码,您可以进行以下更改之一。
onNameChange: function() {
console.log("'this' is ... instance: " + (this.parent.cid === "view2"));
}
或
this.listenTo(
this.model,
"change:name",
$.proxy(this.parent.onNameChange, this.parent)
);
答案 1 :(得分:1)
实际上这是Backbone的一种设计行为。首先让我们看看Backbone如何实现listenTo
。
var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
// Inversion-of-control versions of `on` and `once`. Tell *this* object to
// listen to an event in another object ... keeping track of what it's
// listening to.
_.each(listenMethods, function(implementation, method) {
Events[method] = function(obj, name, callback) {
var listeningTo = this._listeningTo || (this._listeningTo = {});
var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
listeningTo[id] = obj;
if (!callback && typeof name === 'object') callback = this;
obj[implementation](name, callback, this);
return this;
};
});
obj[implementation](name, callback, this);
是魔法发生的地方。当你
this.listenTo(
this.model,
"change:name",
this.parent.onNameChange
);
Backbone实际上将on
个事件监听器添加到this.model
,obj[implementation](name, callback, this);
- &gt; this.model['on']('change:name', this.parent.onNameChange, this)
;
obj[implementation](name, callback, this);
的第3个参数实际上被Backbone称为context
,它会在触发事件时传回回调。
Backbone的on
实现。
on: function(name, callback, context) {
if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
this._events || (this._events = {});
var events = this._events[name] || (this._events[name] = []);
events.push({callback: callback, context: context, ctx: context || this});
return this;
},
以下是tirgger事件的方式
var triggerEvents = function(events, args) {
var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
switch (args.length) {
case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
}
};
查看ev.ctx
变量,这是this
在回调中引用的内容。
因此,如果回调为this.parent.onNameChange
,则this
中的onNameChange
将绑定到错误的对象。