2013-10-16 60 views
0

考慮代碼(also on JSFiddle):爲什麼'this'不能與這個Backbone View保持一致?

// 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實例,因爲在InnerView內調用了listenTo

回答

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

其實這是骨幹的通過設計行爲。首先讓我們看看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 
); 

骨幹實際上是一個on事件偵聽器添加到this.modelobj[implementation](name, callback, this); - >this.model['on']('change:name', this.parent.onNameChange, this);

Backbone的第三個參數obj[implementation](name, callback, this);實際上被稱爲context,當觸發事件時它將被傳回給你的回調。

on執行Backbone。

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,會綁定到錯誤的對象。

相關問題