最近在看些node的源代碼,發現backbone的應用仍是挺普遍的,可是以前的學習忘得一乾二淨了,後悔當時沒作筆記啊。前端
因此,無奈想用的更好,就是得把源代碼看楚,因此仍是把源代碼的註釋筆記留下來,供本身備忘,也供新手學習。node
首先這裏是Backbone.Events的實現代碼註釋,這是backbone的實現mvc的主要保障之一,這種基於事件的機制不管是在前端仍是在後端nodejs部分express
都有着很廣的做用,更況且nodejs的異步機制更須要它來處理一些邏輯問題(固然有人會說promise會比你這種pubsub更適合異步,哈,我只是像代表事件機制的後端
重要性嘛)promise
下面是對代碼的註釋,若是有錯誤還望指出來mvc
1 // Backbone.Events 2 // --------------- 3 4 // A module that can be mixed in to *any object* in order to provide it with 5 // custom events. You may bind with `on` or remove with `off` callback 6 // functions to an event; `trigger`-ing an event fires all callbacks in 7 // succession. 8 // 9 // var object = {}; 10 // _.extend(object, Backbone.Events); 11 // object.on('expand', function(){ alert('expanded'); }); 12 // object.trigger('expand'); 13 // 14 var Events = Backbone.Events = { 15 16 // Bind an event to a `callback` function. Passing `"all"` will bind 17 // the callback to all events fired. 18 on: function(name, callback, context) { 19 // 兩種狀況: 20 // 1. 一次添加多個事件時,經過eventsApi一個一個添加,因此eventsApi返回false,那麼直接return 21 // 2. 回調函數沒定義,沒有意義,直接return 22 if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; 23 // 所以這裏往下事件是一個一個添加的,即name是一個事件名(如:click | custom) 24 // 初始化私有變量,用於存儲事件的信息 25 this._events || (this._events = {}); 26 var events = this._events[name] || (this._events[name] = []); 27 28 // 這裏能夠寫出events的結構了 29 // _events : { 30 // click : [{ 31 // callback : cb1, 32 // context : ctx1, 33 // ctx : ctx1 || this 34 // },{ 35 // callback : cb2, 36 // context : ctx2, 37 // ctx : ctx2 || this 38 // }, ...], 39 // blur : [{...}, {...}, ...], 40 // ... 41 // } 42 events.push({callback: callback, context: context, ctx: context || this}); 43 return this; 44 }, 45 46 // Bind an event to only be triggered a single time. After the first time 47 // the callback is invoked, it will be removed. 48 once: function(name, callback, context) { 49 if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; 50 var self = this; 51 // 將callback進行包裝,返回的新函數newCallback內部會調用calllback, 52 // 不一樣的是,newCallback只會調用callback一次,以後只會返回callback執行的結果 53 // 也就是說once實質上並無去除掉事件監聽函數,而是控制了callback只會執行一次 54 var once = _.once(function() { 55 self.off(name, once); 56 callback.apply(this, arguments); 57 }); 58 // 保留原callback,用於off操做 59 once._callback = callback; 60 // 實質調用.on()方法註冊事件 61 return this.on(name, once, context); 62 }, 63 64 // Remove one or many callbacks. If `context` is null, removes all 65 // callbacks with that function. If `callback` is null, removes all 66 // callbacks for the event. If `name` is null, removes all bound 67 // callbacks for all events. 68 off: function(name, callback, context) { 69 var retain, ev, events, names, i, l, j, k; 70 // 兩種狀況: 71 // 1. 根本沒註冊過事件,何談刪除事件,直接return 72 // 2. 像上述所說支持多事件刪除 73 if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; 74 // 若是是obj.off()這樣調用,那麼刪除該對象obj上全部的事件監聽 75 // 也就是是將_events清空了 76 if (!name && !callback && !context) { 77 this._events = {}; 78 return this; 79 } 80 81 // 若是name爲空,像obj.off(undefined, cb1, ctx1) 82 // 那麼name就爲全部註冊過的事件(即_.keys(this._events)) 83 names = name ? [name] : _.keys(this._events); 84 85 // 根據name遍歷events 86 for (i = 0, l = names.length; i < l; i++) { 87 name = names[i]; 88 if (events = this._events[name]) { 89 this._events[name] = retain = []; 90 91 // 若是callback或者context有一個有值 92 // 那麼接下來將它們做爲條件進行接下來事件的off操做 93 // 實質實際上是先清空_events,將不知足條件刪除條件的事件監聽器從新填入_events中 94 if (callback || context) { 95 for (j = 0, k = events.length; j < k; j++) { 96 ev = events[j]; 97 // 這裏對指定了callback或者context的狀況,作了條件判斷 98 // 這裏的_callback是由於.once方法會對原callback進行包裝,這裏的evn.callback就是包裝後的,原callback保存在_callback中 99 if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || 100 (context && context !== ev.context)) { 101 retain.push(ev); 102 } 103 } 104 } 105 // 發現該事件的事件監聽器被刪光了,那麼作了清理工做,刪除_events對應的key 106 if (!retain.length) delete this._events[name]; 107 } 108 } 109 110 return this; 111 }, 112 113 // Trigger one or many events, firing all bound callbacks. Callbacks are 114 // passed the same arguments as `trigger` is, apart from the event name 115 // (unless you're listening on `"all"`, which will cause your callback to 116 // receive the true name of the event as the first argument). 117 trigger: function(name) { 118 if (!this._events) return this; 119 // 分離出傳給callback的參數 120 var args = slice.call(arguments, 1); 121 // 一樣支持多事件同時trigger 122 if (!eventsApi(this, 'trigger', name, args)) return this; 123 // 該對象該事件的信息 124 var events = this._events[name]; 125 // 該對象all事件的信息(all事件是一個特殊的事件,all事件在其餘事件發生時都會被觸發) 126 var allEvents = this._events.all; 127 // 下面兩部分別是觸發當前事件的回調以及all事件的回調 128 // 不一樣的是,all事件的回調會被多傳遞一個觸發all事件的當前事件的名字做爲參數 129 if (events) triggerEvents(events, args); 130 if (allEvents) triggerEvents(allEvents, arguments); 131 return this; 132 }, 133 134 // Tell this object to stop listening to either specific events ... or 135 // to every object it's currently listening to. 136 stopListening: function(obj, name, callback) { 137 var listeningTo = this._listeningTo; 138 if (!listeningTo) return this; 139 // 140 var remove = !name && !callback; 141 // 這裏是兼容(obj, {click: cb1, change: cb2})這種形式 142 // 保證第三個參數是做爲context傳入,這裏是this 143 if (!callback && typeof name === 'object') callback = this; 144 // 若是有指定obj,那麼解除對象只針對obj 145 // 若是沒有指定,則是解除監聽的全部對象的事件綁定 146 if (obj) (listeningTo = {})[obj._listenId] = obj; 147 for (var id in listeningTo) { 148 obj = listeningTo[id]; 149 obj.off(name, callback, this); 150 // 兩種狀況下作清理工做 151 // 1. 已經代表清除對obj的的全部事件監聽(即name和callback都爲空) 152 // 2. obj對象自身都沒有被綁定事件了,哪來的事件讓你監聽呢? 153 if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; 154 } 155 return this; 156 } 157 158 }; 159 160 // Regular expression used to split event strings. 161 var eventSplitter = /\s+/; 162 163 // Implement fancy features of the Events API such as multiple event 164 // names `"change blur"` and jQuery-style event maps `{change: action}` 165 // in terms of the existing API. 166 // ({}, 'on', 'click blur', [function () {}, undefined]) 167 var eventsApi = function(obj, action, name, rest) { 168 if (!name) return true; 169 170 // Handle event maps. 171 // 支持映射關係 172 // 如:(obj, 'on', {'click': function x () {}, 'blur': function xx () {}}, context) 173 if (typeof name === 'object') { 174 for (var key in name) { 175 // 反覆調用action(on | off | once), 每次添加一個事件監聽,從而達到添加多個。 176 obj[action].apply(obj, [key, name[key]].concat(rest)); 177 } 178 return false; 179 } 180 181 // Handle space separated event names. 182 // 支持空格分割事件(即多事件共享同一個函數) 183 // 如:(obj, 'on', 'click blur', function () {}, context) 184 if (eventSplitter.test(name)) { 185 var names = name.split(eventSplitter); 186 for (var i = 0, l = names.length; i < l; i++) { 187 obj[action].apply(obj, [names[i]].concat(rest)); 188 } 189 return false; 190 } 191 192 return true; 193 }; 194 195 // A difficult-to-believe, but optimized internal dispatch function for 196 // triggering events. Tries to keep the usual cases speedy (most internal 197 // Backbone events have 3 arguments). 198 // 這裏作了個優化,就是若是arg參數在3個之類的話,用call進行調用, 199 // 由於call要比apply的效率高(http://jsperf.com/function-versus-function-call-versus-function-apply) 200 var triggerEvents = function(events, args) { 201 var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; 202 switch (args.length) { 203 case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; 204 case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; 205 case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; 206 case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; 207 default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); 208 } 209 }; 210 211 var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; 212 213 // Inversion-of-control versions of `on` and `once`. Tell *this* object to 214 // listen to an event in another object ... keeping track of what it's 215 // listening to. 216 // 添加listenTo和listernToOnce方法 217 // 實質是: 218 // 1. 給須要監聽的對象obj賦予一個_listenId的隨機id 219 // 2. 再給監聽者(調用對象)添加一個map就是listeningTo屬性,添加上述的id和obj 220 // 3. 給obj綁定被監聽的事件被將this指向調用者 221 // 這裏實質就是調用obj的on或者once方法來添加事件監聽, 222 // 那麼單獨列出這樣的一個方法的好處在於方便監聽者,能夠隨時監聽和解除監聽,上述的1,2兩不操做是爲了之後解除監聽作準備 223 224 _.each(listenMethods, function(implementation, method) { 225 Events[method] = function(obj, name, callback) { 226 var listeningTo = this._listeningTo || (this._listeningTo = {}); 227 var id = obj._listenId || (obj._listenId = _.uniqueId('l')); 228 listeningTo[id] = obj; 229 // 這裏是兼容(obj, {click: cb1, change: cb2})這種形式 230 // 保證第三個參數是做爲context傳入,這裏是this 231 if (!callback && typeof name === 'object') callback = this; 232 obj[implementation](name, callback, this); 233 return this; 234 }; 235 }); 236 237 // Aliases for backwards compatibility. 238 // 向上兼容 239 Events.bind = Events.on; 240 Events.unbind = Events.off; 241 242 // Allow the `Backbone` object to serve as a global event bus, for folks who 243 // want global "pubsub" in a convenient place. 244 // 將Backbone對像擁有事件綁定機制 245 _.extend(Backbone, Events);
各位晚安~~app