既然是源碼學習,那就先把源碼看懂再說,看懂源碼才能進一步的分析。因此,第一步咱們就是逐行讀源碼,可是在讀以前,先交代點事情在前面。javascript
首先,介紹一下我讀源碼的方法。html
讀源碼在讀的過程當中會有一個問題,就是越讀越懵逼。由於源碼都寫的很精簡,會有超多複用的地方。從而,代碼會被拆的很碎,讀着讀着就亂了,就不知道這些代碼在作什麼了。因此,我採用代入法去讀,也能夠說是舉例子的方法。先知道如何使用backbone的事件,寫一個使用的示例。而後,把示例中的變量代入到源碼海量的形參和變量中去,這樣在看源碼的時候就沒那麼抽象了,也清楚每一步在作什麼了。java
舉例說明下代入法,好比咱們要監聽model對象的一個change事件, 觸發view對象的changeHandler方法,代碼以下。api
model.on('change', view.changeHandler, view)
找到on方法的源碼,model和change,changHandler全都代入到代碼中去,代碼以下數組
Events.on = function (name, callback, context) { // internalOn(model, 'change', changeHandler, undefined) return internalOn(this, name, callback, context); };
這樣就是在使用代入法了,這裏只是簡單舉個例子,可能看不出明顯的效果,後續咱們讀大量源碼時候就會感受到它的優點了。 性能優化
其次,本人水平有限,源碼解讀不對的地方,還請多包涵,歡迎批評指正。 app
事情交代完了,正式開始讀源碼吧!函數
首先,總體瞭解下backbone事件部分有哪些方法的代碼要看,請看下圖。post
事件對外暴露的api中,用於綁定事件的有4個方法,可是因爲bind是on的別名,因此實際只有3個方法用來綁定事件,它們分別是on,listenTo,once。用於解綁事件的api中,一樣unbind也是別名,因此實際也是隻要分析off,stopListening就能夠了。觸發事件只有trigger這一個api,畢竟觸發操做一個api就夠了。性能
舉一個例子,監聽model的chang方法,而後觸發view的changHandler方法,代碼以下。代碼中還羅列了其餘幾種on方法的使用方式,爲了方便後續講解代碼中的不少特殊處理。可是,主要仍是使用第一種用法來走通源碼的大部分流程。
model.on('change',view.changeHandler, view) // 更多的用法 一、model.on("change:title change:author", ...); //當回調函數被綁定到特殊"all"事件時,任何事件的發生都會觸發該回調函數,回調函數的第一個參數會傳遞該事件的名稱。 //舉個例子,將一個對象的全部事件代理到另外一對象: 二、proxy.on("all", function(eventName) { object.trigger(eventName); }); // 三、book.on({ "change:title": titleView.update, "change:author": authorPane.update, "destroy": bookView.remove });
將model和view代入源碼中
Events.on = function (name, callback, context) { // internalOn(model, 'change', view.changeHandler, view) return internalOn(this, name, callback, context); };
internalOn,按照命名來解讀是內部使用的on方法的意思,在其餘地方還會複用。
var internalOn = function (obj, name, callback, context, listening) { // model._events = // eventsApi(onApi, model._events || {}, 'change', view.changeHandler, { // context: view, // ctx: model, // listening: undefined // }) obj._events = eventsApi(onApi, obj._events || {}, name, callback, { context: context, // undefined ctx: obj, // model listening: listening // undefined }); // undefined 不會執行 if (listening) { var listeners = obj._listeners || (obj._listeners = {}); listeners[listening.id] = listening; } return obj; };
internalOn 爲model這個被監聽的對象賦一個_events屬性,並調用eventsApi方法對_events屬性作了處理。因爲on方法沒有傳入listening屬性,if(listening)裏面的代碼放到後續分析,咱們先主要看一下這個eventsApi作了什麼。
// model._events = // eventsApi(onApi, model._events || {}, 'change', view.changeHandler, { // context: view, // ctx: model, // listening: undefined // }) var eventsApi = function (iteratee, events, name, callback, opts) { var i = 0, names; if (name && typeof name === 'object') { // 處理上述更多用法中的第3種,傳入包含事件名和對應回調函數的對象 // model.on({ // "change": view.changeHandler, // "change:author": authorPane.update, // "destroy": bookView.remove // }); //這裏`void 0`表明undefined // 若是傳入了callback,opts中的context屬性爲undefined,那麼把callback賦值給opts.context if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; // 遍歷傳入的事件對象 for (names = _.keys(name); i < names.length; i++) { // 將對象中的事件和事件回調分別取出 // 轉換爲 model.on('change',view.changeHandler, view)使用方式調用eventsApi函數的方式 // events = eventsApi(onApi, model._events, 'change', view.changeHandler, opts); // events = eventsApi(onApi, model._events, 'change:author', authorPane.update, opts); // events = eventsApi(onApi, model._events, 'destroy', bookView.remove, opts); // 而後去執行下面else if 或者 else那種狀況的代碼 events = eventsApi(iteratee, events, names[i], name[names[i]], opts); } } else if (name && eventSplitter.test(name)) { // 處理上述的第1種寫法 // model.on("change change:author", ...); for (names = name.split(eventSplitter); i < names.length; i++) { // events = onApi(model._events, 'change', view.handler, opts) // events = onApi(model._events, 'change: author', view.handler, opts) events = iteratee(events, names[i], callback, opts); } } else { // 最簡單的寫法,上述最上面那種寫法 // model.on('change',view.changeHandler, view) // events = onApi(model._events, 'change', view.changeHandler, opts); events = iteratee(events, name, callback, opts); } return events; };
經過看上面代碼和註釋,應該就能夠得出一個結論,這個eventsApi方法就是用來處理各類不一樣的用法傳入的參數,最終統一的使用iteratee(model._events, 'change', view.changeHandler, opts);來處理model._events。
繼續回到internalOn方法
obj._events = eventsApi(onApi, obj._events || {}, name, callback, { context: context, ctx: obj, listening: listening });
上面的代碼就能夠轉化爲
model._events = onApi(model._events, 'change', view.changeHandler,{ context: view, ctx: model, listening: undefined });
這裏面又調用了onApi方法,咱們傳入上面代碼裏的參數來看一下
var onApi = function (events, name, callback, options) { // view.changeHandler if (callback) { // 若是 model._events['change'] 不存在,執行 model._events['change'] = [] var handlers = events[name] || (events[name] = []); // context = view ctx = model listening = undefined var context = options.context, ctx = options.ctx, listening = options.listening; // listening爲空,這裏暫時沒用到,後續其餘方法涉及再講解 if (listening) listening.count++; handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening }); } return events; // model._events = { // change: [ // { callback: view.changeHandler, context: view, ctx: model, listening: undefined } // ] // } };
經過執行onApi最終獲得了新的model._events對象,也就是on方法裏面的代碼爲了綁定監聽事件而設計的數據模型。
model._events = { change: [ { callback: view.changeHandler, context: view, ctx: model, listening: undefined } ] }
整個代碼其實都是在圍繞打造這個數據模型。暫且先把這個數據模型放在這裏,咱們接着來看下一個綁定事件的方法listenTo。
這裏有篇大神的文章對listenTo有一個很清晰的講解,裏面也涵蓋了不少backbone事件部分心法方面的內容,文章寫得很好,我就再也不贅述了,你們最好看一下。心法配着源代碼看,纔會真正有收貨。
仍是先舉一個用法例子,view對象監聽model的change事件,觸發view的changeHandler方法
view.listenTo(model,'change',view.changeHandler)
來看listenTo方法的源碼
//執行view.listenTo(model,'change',changeHandler) Events.listenTo = function (obj, name, callback) { // obj=model if (!obj) return this; // model._listenId 不存在,執行 model._listenId = _.uniqueId('l') == 'l1' // model給本身添加一個屬性_listenId,等於給本身打一個惟一標記 var id = obj._listenId || (obj._listenId = _.uniqueId('l')); // view._listeningTo不存在,執行 view._listeningTo = {} var listeningTo = this._listeningTo || (this._listeningTo = {}); // listening = view._listeningTo[model._listenId] // 這裏設計的挺巧妙,語義化也很好,理解起來就是view對象正在監聽着一個惟一id var listening = listeningTo[id]; // 若是 view._listeningTo[model._listenId] 正在監聽的這個惟一id(key)對應的對象(value)不存在 if (!listening) { // view先給本身也打一個惟一標記 view._listenId = _.uniqueId('l') == 'l2' var thisId = this._listenId || (this._listenId = _.uniqueId('l')); // 把view本身和被監聽對象的信息全都存起來,存到正在監聽某id這個屬性中去 listening = listeningTo[id] = { obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0 }; // 獲得一個數據模型,這個數據模型在view的屬性上面,_listeningTo // view._listeningTo: { // l2: { // obj: model, // objId: model._listenId, // id: view._listenId, // listeningTo: view._listeningTo, // count: 0 // } // } } // internalOn(obj, name, callback, this, listening); return this; };
這個方法在view和model上都添加了_listenId屬性,用來添加惟一標識,惟一標識是經過underscore的uniqueId產生的,backbone是依賴了underscore的。不只添加了惟一標識,還在監聽者身上添加了一個_listeningTo屬性,保存了一個重要的數據模型。這個數據模型爲何要這麼設計,後續咱們就會知道。
view._listeningTo: { l2: { obj: model, objId: model._listenId, id: view._listenId, listeningTo: view._listeningTo, count: 0 } }
獲得這個數據模型以後,執行了internalOn方法,這個方法在on方法中咱們分析過,不一樣的是此次傳入了listening,咱們看看會發生什麼。
// Guard the `listening` argument from the public API. var internalOn = function (obj, name, callback, context, listening) { // model._events obj._events = eventsApi(onApi, obj._events || {}, name, callback, { context: context, // view ctx: obj, // model listening: listening //view._listeningTo[model._listenId] }); // listening存在 執行 if (listening) { // model._listeners不存在,執行model._listeners = {} var listeners = obj._listeners || (obj._listeners = {}); // model._listeners[view._listenId] = view._listeningTo[model._listenId] // model._listeners['l2'] = view._listeningTo['l1'] listeners[listening.id] = listening; } return obj; };
複用interOn方法,其實就是執行了和on方法相同的處理邏輯來綁定監聽,爲model這個被監聽者綁定事件屬性_events。
var onApi = function (events, name, callback, options) { if (callback) { // events = model._events var handlers = events[name] || (events[name] = []); var context = options.context, ctx = options.ctx, listening = options.listening; // view._listeningTo[model._listenId] // count ++ if (listening) listening.count++; // push的listening第一個數據中的count爲1 handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening }); } return events; };
因此,model._events會拿到和on方法大體相同的數據模型。先回顧一下on方法那裏拿到的_events,以下。
model._events = { change: [ { callback: view.changeHandler, context: view, ctx: model, listening: undefined } ] }
可是因爲listenTo方法這裏的listening參數再也不爲空,因此會獲得下面的這種_events
model._listenId = 'l1' view._listenId = 'l2' model._events = { 'change': [ { callback: view.changeHandler, context: view, ctx: view || model, listening: view._listeningTo['l1'] } ] }
listening這個屬性我理解爲 「如今的監聽狀況:view正在監聽向model(l1)」。不只是eventsApi裏面,外面的if(listening)也由於listening的賦值能夠執行了。執行的結果是爲model也添加了一個屬性_listeners(監聽者),獲得一個數據模型,以下。
model._listenId = 'l1' view._listenId = 'l2' model._listeners = { 'l2': view._listeningTo['l1'] }
這個監聽者,語義化也是很好,很是好理解,「監聽者view(l2):view正在監聽着model(li)」。
那麼這個listenTo方法執行下來,獲得的全套數據模型以下。
{ view.listenTo(model, 'change', view.changeHandler) model._listenId = 'l1' view._listenId = 'l2' model._events = { 'change': [ { callback: view.changeHandler, context: view, ctx: view || model, listening: view._listeningTo['l1'] } ] } model._listeners = { 'l2': view._listeningTo['l1'] } view._listeningTo = { 'l1': { obj: model, objId: 'l1', id: 'l2', listeningTo: view._listeningTo, count: 1 } }
若是多寫幾個監聽,就會在這個數據模型基礎上擴展,好比
//添加model2 view.listenTo(model2, 'change', changeHandler) model2._listenId = 'l3' model2._listeners = { 'l2': view._listeningTo['l3'] } view._listeningTo = { 'l1': { obj: model, objId: 'l1', id: 'l2', listeningTo: view._listeningTo, count: 1 }, 'l3': { obj: model2, objId: 'l3', id: 'l2', listeningTo: view._listeningTo, count: 1 } } //添加view2 view2.listenTo(model, 'change', changeHandler) // 同理 model._listeners的key也會多一個
這個數據模型裏面的count要多說幾句。看了on和listenTo代碼能夠發現,同一個事件名能夠屢次添加相同的callback。就好比view.listenTo(model, a),這個代碼能夠執行屢次,而後每一次都在model._events裏面添加進去了。相應的,在model._listeners和view._listeningTo裏面因爲是key惟一的對象,多添加幾回也只是會覆蓋,體現不出屢次添加。因此,就在key裏面的對象的count那裏加1,以表示屢次。
上面的全部代碼的組織都應該是先設計好這些基礎的數據模型,而後圍繞這些模型編寫的。編寫代碼過程當中天然會發現數據模型的不足,進而再對數據模型進行補充,最終呈現出如今的代碼形態。
上面咱們介紹了2種事件綁定方式on和listenTo,還差一種once沒有解讀。因爲once比較特殊,本身綁定事件後,執行一次即本身解綁事件,因此放在後面解讀。先看一下如何觸發事件,更有助於理解數據模型的設計。
先看看怎麼使用trigger
// 隨便傳了個1 2 model.trigger('change', 1, 2)
把綁定的數據模型拿來對照一下,方便理解。
// on model._events = { change: [ { callback: view.changeHandler, context: view, ctx: model, listening: undefined } ] } // listenTo model._events = { 'change': [ { callback: view.changeHandler, context: view, ctx: view || model, listening: view._listeningTo['l1'] } ] }
上trigger源碼
Events.trigger = function (name) { if (!this._events) return this; var length = Math.max(0, arguments.length - 1); var args = Array(length); // 將name以外的傳參所有存到args數組中 for (var i = 0; i < length; i++) args[i] = arguments[i + 1]; // name能夠爲事件對象形式、帶空格字符串形式以及最簡單的事件名字符串形式 // 事件對象這種形式是能夠處理的,可是沒人會這麼傳吧,畢竟是要去觸發事件,搞這麼複雜幹啥 // 使用 eventsApi 方法扁平化,統一處理方式 // 傳入triggerApi, model._events 'change' undefined [1, 2] eventsApi(triggerApi, this._events, name, void 0, args); return this; };
觸發函數的入參和綁定事件的函數是一致的,由於都調用了eventsApi方法去統一處理方式。
eventsApi這個方法會把參數傳給triggerApi方法去執行,主要的觸發邏輯也是在triggerApi中,咱們就來看一下是怎麼處理的。
//對trigger進行進一步處理,好比區分是否監聽了all事件 var triggerApi = function (objEvents, name, callback, args) { // model._events if (objEvents) { // model._events.change var events = objEvents[name]; //處理對all事件進行監聽的狀況 var allEvents = objEvents.all; if (events && allEvents) allEvents = allEvents.slice(); // 若是綁定了這個事件,觸發這個事件 if (events) triggerEvents(events, args); // 若是綁定了all事件,觸發all事件 // 假設A對象監聽了B對象的all事件,那麼全部的B對象的事件都會被觸發,而且會把傳入的事件名做爲第一個函數參數 if (allEvents) triggerEvents(allEvents, [name].concat(args)); } return objEvents; };
這裏又調用了triggerEvents方法,在裏面調用了callback方法,完成了觸發。
/* 對事件進行觸發,優先進行call調用,call調用比apply調用效率更高,因此優先進行call調用 這裏的events參數,其實是回調函數列 */ var triggerEvents = function (events, args) { // a1 a2 a3是由於認爲call最多調用3個參數,超過3個就使用apply,一種性能優化的考慮,優先進行call調用 var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; switch (args.length) { // 這裏終於用到了ctx 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; //由於call調用的時候是須要將參數展開的,而apply調用的時候傳入一個數組便可 default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; } };
先看一下使用方式,有點多,有點複雜,狀況畢竟覆蓋的多。
Removes just the `onChange` callback. object.off("change", onChange); Removes all "change" callbacks. object.off("change"); Removes the `onChange` callback for all events. object.off(null, onChange); Removes all callbacks for `context` for all events. object.off(null, null, context); Removes all callbacks on `object`. object.off();
off方法源碼以下
Events.off = function (name, callback, context) { if (!this._events) return this; this._events = eventsApi(offApi, this._events, name, callback, { context: context, listeners: this._listeners }); return this; };
根據上面幾種用法,context能夠傳也能夠不傳。eventsApi這個方法真是超高複用了,這裏就不贅述了,直接看offApi幹了什麼吧。
先上offApi要對付的數據模型,有的放矢。
//on model._events = { change: [ { callback: view.changeHandler, context: view, ctx: model, listening: undefined } ] } // listenTo model._events = { 'change': [ { callback: changeHandler, context: view, ctx: view || model, listening: view._listeningTo['l1'] } ] } model._listeners = { 'l2': view._listeningTo['l1'] } view._listeningTo = { 'l1': { obj: model, objId: 'l1', id: 'l2', listeningTo: view._listeningTo, count: 1 } }
再來看offApi
var offApi = function (events, name, callback, options) { if (!events) return; var i = 0, listening; // context listeners = model._listeners var context = options.context, listeners = options.listeners; // model.off() if (!name && !callback && !context) { // 有listeners的狀況,就是用listenTo綁定事件,處理model._listeners var ids = _.keys(listeners);//全部監聽它的對應的屬性 for (; i < ids.length; i++) { listening = listeners[ids[i]]; // 刪除 model._listeners[id] delete listeners[listening.id]; // 刪除 view._listeningTo[id] delete listening.listeningTo[listening.objId]; } //這個offApi最終是要返回events,return 等因而 model._events 置空了 return; } // 若是傳入了name,好比change,返回['change'] // 沒有name 獲取model._events的鍵值,拿到全部的事件名 var names = name ? [name] : _.keys(events); for (; i < names.length; i++) { name = names[i]; // 遍歷事件名,拿到事件對應的對象 var handlers = events[name]; //若是沒有回調函數,直接break if (!handlers) break; // Replace events if there are any remaining. Otherwise, clean up. var remaining = []; // for (var j = 0; j < handlers.length; j++) { var handler = handlers[j]; //這裏要嚴格對上下文進行判斷,上下文不等不能刪除 if ( callback && callback !== handler.callback && callback !== handler.callback._callback || context && context !== handler.context ) { // callback傳了,可是和綁定的對不上 或者 context傳了,可是和綁定的對不上 // 就保留下來 remaining.push(handler); } else { // callback或者context傳入了,而且callback或者context對的上 listening = handler.listening; // 那麼就處理view._listeningTo[id] // 以前說了count值就表明屢次綁定,count-1就表明去掉一次,若是--count===0了就說明是最後一個了 // 就直接刪掉屬性值了 if (listening && --listening.count === 0) { delete listeners[listening.id]; delete listening.listeningTo[listening.objId]; } } } // Update tail event if the list has any events. Otherwise, clean up. if (remaining.length) { events[name] = remaining; } else { delete events[name]; } } return events; };
off刪除仍是挺簡單的,只要掌握了綁定事件構建的數據模型,針對不一樣的用法理解不一樣的判斷處理,就能夠很簡單的看懂了。
stopListening也是用來解除綁定的,爲何有了off還要有stopListening呢?看一下上面的off源碼就能夠發現,off的用法是做用在被監聽的對象上一個一個的去解除監聽。舉個例子來講,來個一目瞭然。
var view = { changeName :function(name){ //doing something } } model.on('change:name',view.changeName,view); model2.on('change:name',view.changeName,view); //view離開時,model如何解綁 model.off('change:name',view.changeName,view); model2.off('change:name',view.changeName,view);
有多個model的話,須要進行屢次的解綁操做。再來看看stopListening的解綁。
view.listenTo(model,'change:name',view.changeName); view.listenTo(model2,'change:name',view.changeName); //解綁 model.off('change:name',view.changeName) model2.off('change:name',view.changeName) //解綁 view.stopListening();
並不須要作更多的操做就能把view相關的監聽事件所有給解綁。
瞭解了用法,咱們來看下源碼是怎麼實現的。
// view.listenTo(model, 'change:name', view.changeName); // view.listenTo(model2, 'change:name', view.changeName); // 至關於 model.on('change:name', view.changeName, view) // view.stopListening(); // view.stopListening(model, 'change:name', view.changeName) Events.stopListening = function (obj, name, callback) { // view._listeningTo var listeningTo = this._listeningTo; if (!listeningTo) return this; //若是沒有指定obj,就解綁全部的對別的對象的事件監聽,若是指定了obj,就解綁對應obj的 var ids = obj ? [obj._listenId] : _.keys(listeningTo); for (var i = 0; i < ids.length; i++) { var listening = listeningTo[ids[i]]; // 這裏進行檢查,若是壓根就沒有監聽,實際上說明用這個函數是畫蛇添足的,這裏直接break就好(而不是continue) if (!listening) break; //這裏直接用了off方法,並傳遞正確的this上下文(爲監聽者) // off用於解綁被監聽者 // model.on('change:name', view.changeName, view) === view.listenTo(model2, 'change:name', view.changeName); // model.off listening.obj.off(name, callback, this); } return this; };
看源碼可知,stopListening只是針對使用listenTo的監聽者使用的,爲的就是便捷的所有解綁監聽者全部的監聽,而沒必要去屢次調用off。可是其實stopListening的底層仍然是使用的off方法去解綁被監聽者的,這裏也是體現了複用的思想。從源碼中也能夠看出爲何數據模型中view.listeningTo中的對象要包含obj這個屬性,在解綁這裏被用到了。數據模型中的不少感受多餘的不可理解的屬性,都是在擴展功能的過程當中不斷添加進去的,我是這麼理解的。
once用法跟on很像,區別在於綁定的回調函數觸發一次後就會被移除(注:只執行一次)。簡單的說就是「下次再也不觸發了,用這個方法」。
model.once('change', view.changeHandler, view)
Events.once = function (name, callback, context) { var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); if (typeof name === 'string' && context == null) callback = void 0; // this.on({'change': once_callback}, callback, context) return this.on(events, callback, context); };
看到這個方法很容易能夠看到不一樣之處。在eventsApi這個方法調用的時候,傳入的events是一個空對象,而不是model._events。最後一個參數傳入的也不是opts,而是綁定好上下文的off方法,上下文綁定的是model。
那麼,在執行onceMap,返回的events是什麼呢。
var onceMap = function (map, name, callback, offer) { if (callback) { // 自成一體,執行後自行解綁,不須要off等方法解綁 // once變量 和 map[name]均指向_.once(function(){}) var once = map[name] = _.once(function () { offer(name, once); callback.apply(this, arguments); }); //這個在解綁的時候有一個分辨效果 once._callback = callback; } return map; };
onceMap中的_.once方法返回的是一個只能執行一次的函數,這裏直接使用了underscore。這個返回的函數指向事件對象的change屬性,而且函數上添加了一個_callback屬性,真正的傳入的callback函數指向了_callback屬性。_.once中的回調函數,解綁了once函數,而且執行了callback回調函數。返回的events以下
Events.once = function (name, callback, context) { var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); // events = { // change: once_callback // } // once_callback = _.once(function () { // 僞代碼,只是表達個意思,觸發事件以後解綁函數 // off(name, once); // callback.apply(this, arguments); // }); // once_callback._callback = callback if (typeof name === 'string' && context == null) callback = void 0; // this.on({'change': once_callback}, callback, context) return this.on(events, callback, context); };
事件變量傳入到on方法,完成綁定。off解綁那裏的_callback,呼應了off源碼,經過檢驗_callback來判斷如何作下一步操做。
if ( callback && callback !== handler.callback && callback !== handler.callback._callback || context && context !== handler.context ) {
on完成綁定以後,觸發事件,執行相應的callback,off解除綁定,就是這樣一波操做。
參考:《Backbone系列篇之Backbone.Events源碼解析》 http://www.javashuo.com/article/p-daudqxbt-gq.html
《Backbone.js(1.1.2) API中文文檔》 https://www.html.cn/doc/backbone/#Events-on