摘要:接受個(gè)參數(shù),包括事件的名稱,回調(diào)函數(shù)和回調(diào)函數(shù)執(zhí)行的上下文環(huán)境。保留回調(diào)函數(shù)在數(shù)組中取出對(duì)應(yīng)的以及中的函數(shù)。當(dāng)然,你同樣可以在綁定的回調(diào)函數(shù)執(zhí)行前手動(dòng)通過(guò)將其移除。
Backbone源碼解讀
Backbone在流行的前端框架中是最輕量級(jí)的一個(gè),全部代碼實(shí)現(xiàn)一共只有1831行1。從前端的入門(mén)再到Titanium,我雖然幾次和Backbone打交道但是卻對(duì)它的結(jié)構(gòu)知之甚少,也促成了我想讀它的代碼的原始動(dòng)力。這個(gè)系列的文章主要目的是分享Backbone框架中可以用于日常開(kāi)發(fā)的實(shí)踐參考,力求能夠簡(jiǎn)明扼要的展現(xiàn)Backbone Modal, Controller和Sync這些核心內(nèi)容,希望能夠?qū)Υ蠹覍W(xué)習(xí)和使用Backbone有一些幫助。
這個(gè)系列的文章將包括以下3篇內(nèi)容:
Backbone源碼解讀之Events實(shí)現(xiàn)
Backbone源碼解讀之Router, History實(shí)現(xiàn)
Backbone源碼解讀之Model, Collection, Sync實(shí)現(xiàn)
本文是它的第一篇《Backbone源碼解讀之Events實(shí)現(xiàn)》
Backbone Events實(shí)現(xiàn)Backbone的Event是整個(gè)框架運(yùn)轉(zhuǎn)的齒輪。它的優(yōu)美之處在于它是Backbone的一個(gè)基礎(chǔ)方法,通過(guò)_.extend的方法Mixin到Backbone的每一個(gè)模塊中。
//Model _.extend(Model.prototype, Events, { changed: null, //other Model prototype methods //... }Event的基礎(chǔ)概念
事件的管理是一個(gè)綁定在Events這個(gè)命名空間中的_events對(duì)象來(lái)實(shí)現(xiàn)的。
它的結(jié)構(gòu)是name: [callback functions]的key-callback_array鍵值對(duì)。
this._events = { change: [callback_on_change1, callback_on_change2, ....], .... }
當(dāng)事件發(fā)生的時(shí)候,event從這個(gè)對(duì)象中根據(jù)事件的名稱取得回調(diào)函數(shù)數(shù)組,然后循環(huán)執(zhí)行每個(gè)回調(diào)函數(shù),也就說(shuō)明了為什么多次綁定會(huì)重復(fù)觸發(fā)多次事件。
Event包括on, off, trigger三個(gè)基礎(chǔ)方法,其余的所有方法均是對(duì)它們的擴(kuò)展。
on(name, callback, context)on接受3個(gè)參數(shù),包括事件的名稱,回調(diào)函數(shù)和回調(diào)函數(shù)執(zhí)行的上下文環(huán)境。其中context是可選參數(shù),如果你不是很熟悉JS的執(zhí)行上下文環(huán)境可以暫時(shí)不用管它。
拋開(kāi)所有Backbone的花哨的檢查,執(zhí)行on的操作本質(zhì)就是向_events中name對(duì)應(yīng)的回調(diào)函數(shù)數(shù)組[callback functions]中Push新的函數(shù)。
簡(jiǎn)單來(lái)說(shuō)代碼實(shí)現(xiàn)就是這個(gè)樣子:
Events.on = function(name, callback, context) { if (callback) { var handlers = events[name] || (events[name] = []); handlers.push({callback: callback, context: context, ctx: context || this}); } return this; };
至于你在看源代碼的時(shí)候會(huì)長(zhǎng)很多,那是因?yàn)橐环矫鍮ackbone要處理關(guān)于_events以及_events[name]未初始化的兩種特殊情況。另一方面eventsApi,onApi這些方法是為了處理on時(shí)候你傳入的不是一個(gè)string類(lèi)型的名稱和一個(gè)callback函數(shù)所做的條件處理。
例如下面兩種方法都是合法的:
//傳入一個(gè)名稱,回調(diào)函數(shù)的對(duì)象 model.on({ "change": on_change_callback, "remove": on_remove_callback }); //使用空格分割的多個(gè)事件名稱綁定到同一個(gè)回調(diào)函數(shù)上 model.on("change remove", common_callback);
但是核心其實(shí)都是同一個(gè)綁定函數(shù)。
值得注意的一點(diǎn)是由于Backbone接受all作為name的參數(shù),并且將回調(diào)函數(shù)保存在_events.all中,關(guān)于它的執(zhí)行詳細(xì)可以參考trigger。
off(name, callback, context)與on不同,off的3個(gè)參數(shù)都是可選的。
如果沒(méi)有任何參數(shù)的時(shí)候,off相當(dāng)于把對(duì)應(yīng)的_events對(duì)象整體清空。
if (!name && !callback && !context) { this._events = void 0; return this; }
如果有name參數(shù)但是沒(méi)有具體清除哪個(gè)callback的時(shí)候,則把_events[name]對(duì)應(yīng)的內(nèi)容全部清空。
if (!callback && !context) { delete this._events[name]; continue; }
如果還有進(jìn)一步詳細(xì)的callback和context的情況下,則進(jìn)入[callback functions]中進(jìn)行檢查,移除具體一個(gè)回調(diào)函數(shù)的條件非常嚴(yán)苛,必須要求上下文和函數(shù)與原來(lái)完全一致,也就是說(shuō)如果傳入的callback或者context不是原有on對(duì)象的引用,而是復(fù)制的話,這里的off是無(wú)效的。
var remaining = []; if( callback && callback !== handler.callback && callback !== handler.callback._callback || context && context !== handler.context ){ //保留回調(diào)函數(shù)在數(shù)組中 }trigger(name)
trigger取出name對(duì)應(yīng)的_events[name]以及_event.all中的callback函數(shù)。
需要注意的一點(diǎn)是,觸發(fā)對(duì)應(yīng)名稱的callback和all的callback使用了不一樣的參數(shù),all的參數(shù)中還包含了當(dāng)前事件的名稱。
//當(dāng)綁定3個(gè)以下回調(diào)函數(shù)的時(shí)候Backbone會(huì)做如下優(yōu)化處理,據(jù)說(shuō)這樣是可以提高執(zhí)行效率的。 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); return; } };最簡(jiǎn)單的Backbone事件使用
從使用上來(lái)講,對(duì)一個(gè)對(duì)象進(jìn)行事件的on綁定。然后在同一個(gè)對(duì)象的其他函數(shù)執(zhí)行過(guò)程中,或者其他的對(duì)象中,觸發(fā)該對(duì)象的trigger方法和對(duì)應(yīng)的事件名稱來(lái)執(zhí)行其上綁定的callback函數(shù)。
其他輔助函數(shù)接下來(lái)再看看Event中進(jìn)一步定義了哪些其他輔助函數(shù)
once(name, callback, context)效果相同與on,不過(guò)對(duì)應(yīng)的callback函數(shù)僅執(zhí)行一次。當(dāng)然,你同樣可以在once綁定的回調(diào)函數(shù)執(zhí)行前手動(dòng)通過(guò)off將其移除。
來(lái)看看Once的實(shí)現(xiàn),由于添加了執(zhí)行過(guò)程中的移除方法,once在實(shí)際實(shí)行on的時(shí)候使用了如下匿名函數(shù):
var once = _.once(function() {function(){ self.off(name, once); callback.apply(this, arguments); }); return this.on(name, once, context);
但是細(xì)心的你一定發(fā)現(xiàn),保存在_event數(shù)組中的函數(shù)是once這個(gè)匿名函數(shù)了。但是用戶并不知道Backbone的這些操作,在取消綁定時(shí)仍然會(huì)使用原來(lái)的回調(diào)函數(shù)來(lái)試圖解除綁定。上面我們也提到,必須使用完全一致的函數(shù)才能夠取消綁定,那么為什么還能夠成功呢?
這里Backbone做了一個(gè)小小的操作,不知道你有沒(méi)有注意到上面off函數(shù)中有這樣一行內(nèi)容?
callback !== handler.callback._callback
既然callback是我們傳入的回調(diào)函數(shù),那么哪里來(lái)的_callback這個(gè)屬性呢?答案就在once里面。
var once = _.once(function() {...取消綁定,執(zhí)行callback); once._callback = callback;
也就是Backbone在返回之前悄悄的為once這個(gè)函數(shù)添加了一個(gè)_callback的屬性,用來(lái)保存原來(lái)的回調(diào)函數(shù),這樣用戶在傳入原來(lái)的回調(diào)函數(shù)取消綁定的時(shí)候,off會(huì)檢查函數(shù)時(shí)候有_callback這個(gè)屬性和用戶傳入的函數(shù)匹配,同樣可以取消綁定。
listenTo(obj, name, callback)、listenToOnce(obj, name, callback)和stopListening(obj, name, callback)除了將對(duì)象本身expose給另一個(gè)對(duì)象,讓另一個(gè)對(duì)象執(zhí)行trigger方法觸發(fā)該對(duì)象上綁定的event以外。Event還進(jìn)一步提供了listenTo系列的方法,執(zhí)行邏輯正好與on相反。
例如有如下要求,當(dāng)B對(duì)象上發(fā)生事件b的時(shí)候,觸發(fā)A對(duì)象的callbackOnBEvent函數(shù)。
// 使用on的情況下 B.on(“b”, A.callbackOnBEvent) // 使用listenTo的情況下 A.listenTo(B, “b”, callbackOnEvent);
從實(shí)現(xiàn)上看,它門(mén)的區(qū)別就在于誰(shuí)負(fù)責(zé)管理這個(gè)事件。第一個(gè)模型中,B就像是整個(gè)系統(tǒng)的master,負(fù)責(zé)事件到達(dá)的時(shí)候的分發(fā),讓不同的對(duì)象(如A)執(zhí)行對(duì)應(yīng)的方法。第二個(gè)模型中,B更像是一個(gè)信息棧,A監(jiān)聽(tīng)B上發(fā)生的事件,并且在對(duì)應(yīng)事件到達(dá)的時(shí)候觸發(fā)自身相應(yīng)的回調(diào)函數(shù)。兩者并無(wú)好壞之分,但是從系統(tǒng)架構(gòu)上來(lái)說(shuō)因?yàn)楸旧砘卣{(diào)函數(shù)的上下文環(huán)境就是A,所以listenTo的方式可能會(huì)來(lái)的更加自然,而且由A自己來(lái)控制什么時(shí)候移除回調(diào)的執(zhí)行,也可以讓代碼的解耦程度更高。
超越Backbone使用Event方法來(lái)處理異步請(qǐng)求讓代碼的可讀性大大增加。如果你的單頁(yè)面應(yīng)用恰好使用了Backbone作為前端框架,將Event通過(guò)Backbone.Events這個(gè)變量暴露出來(lái),你可以使用類(lèi)似Model擴(kuò)展的方法
//Your object _.extend(your_object.prototype, Backbone.Events, { //other prototype methods //... }
這樣你的Object也就具有了彼此綁定事件、觸發(fā)事件的能力。
即便你的前端并沒(méi)有使用Backbone,由于Events并不依賴Backbone的其他部分實(shí)現(xiàn),你完全可以將它放到自己的代碼lib中,作為一個(gè)基礎(chǔ)方法來(lái)使用。
類(lèi)似的方式你也可以經(jīng)常在Node的后端看到
var util = require("util"); var events = require("events"); function MyStream() { events.EventEmitter.call(this); } util.inherits(MyStream, events.EventEmitter);
總之,我個(gè)人是非常推薦多多使用Event來(lái)替代層級(jí)的Callback結(jié)構(gòu)。
根據(jù)2015年4月 穩(wěn)定版本Backbone.js 1.1.2的注釋版本。Master上的代碼和注釋版本稍有出入,哪位大神知道為什么嗎????
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/85600.html
1. 開(kāi)場(chǎng) 1.1 MVC? MVC是一種GUI軟件的一種架構(gòu)模式。它的目的是將軟件的數(shù)據(jù)層(Model)和視圖(view)分開(kāi)。Model連接數(shù)據(jù)庫(kù),實(shí)現(xiàn)數(shù)據(jù)的交互。用戶不能直接和數(shù)據(jù)打交道,而是需要通過(guò)操作視圖,然后通過(guò)controller對(duì)事件作出響應(yīng),最后才得以改變數(shù)據(jù)。最后數(shù)據(jù)改變,通過(guò)觀察者模式更新view。(所以在這里需要用到設(shè)計(jì)模式中的觀察者模式) 1.2 Smalltalk-80...
摘要:以為例構(gòu)造函數(shù)的內(nèi)容構(gòu)造函數(shù)的內(nèi)部一般會(huì)做以下幾個(gè)操作各種給內(nèi)部對(duì)象設(shè)置屬性。為什么呢源碼做出了解釋。在里面會(huì)調(diào)用用戶傳入的回調(diào)函數(shù)并觸發(fā)事件表示已經(jīng)同步了。整個(gè)的源碼事實(shí)上就是這兩組東西。 1. 開(kāi)場(chǎng) 強(qiáng)烈建議一邊看著源碼一邊讀本文章,本文不貼大段代碼。源碼地址。在寫(xiě)backbone應(yīng)用的時(shí)候,說(shuō)實(shí)話,大部分的時(shí)間都是在寫(xiě)這三個(gè)模塊的內(nèi)容。關(guān)于這三個(gè)模塊的分析網(wǎng)上隨隨便便就能找到一堆...
摘要:個(gè)人認(rèn)為,讀懂老牌框架的源代碼比會(huì)用流行框架的要有用的多。另外,源代碼中所有的以開(kāi)頭的方法,可以認(rèn)為是私有方法,是沒(méi)有必要直接使用的,也不建議用戶覆蓋。 寫(xiě)在前面 backbone是我兩年多前入門(mén)前端的時(shí)候接觸到的第一個(gè)框架,當(dāng)初被backbone的強(qiáng)大功能所吸引(當(dāng)然的確比裸寫(xiě)js要好得多),雖然現(xiàn)在backbone并不算最主流的前端框架了,但是,它里面大量設(shè)計(jì)模式的靈活運(yùn)用,以及令...
摘要:個(gè)人認(rèn)為,讀懂老牌框架的源代碼比會(huì)用流行框架的要有用的多。另外,源代碼中所有的以開(kāi)頭的方法,可以認(rèn)為是私有方法,是沒(méi)有必要直接使用的,也不建議用戶覆蓋。 寫(xiě)在前面 backbone是我兩年多前入門(mén)前端的時(shí)候接觸到的第一個(gè)框架,當(dāng)初被backbone的強(qiáng)大功能所吸引(當(dāng)然的確比裸寫(xiě)js要好得多),雖然現(xiàn)在backbone并不算最主流的前端框架了,但是,它里面大量設(shè)計(jì)模式的靈活運(yùn)用,以及令...
摘要:最近對(duì)主要對(duì)源碼進(jìn)行了閱讀,分別解讀了,,目錄如下希望大家能互相交流 最近對(duì)backbone主要對(duì)源碼進(jìn)行了閱讀,分別解讀了Backbone.Model,Backbone.View , Backbone.Collection,Backbone.Event,目錄如下: Backbone.View Backbone.Collection Backbone.Model Backbone....
閱讀 1427·2023-04-25 23:42
閱讀 3137·2021-11-19 09:40
閱讀 3623·2021-10-19 11:44
閱讀 3831·2021-10-14 09:42
閱讀 2033·2021-10-13 09:39
閱讀 3959·2021-09-22 15:43
閱讀 744·2019-08-30 15:54
閱讀 1528·2019-08-26 13:32