【原創】backbone1.1.0源碼解析之Events

最近在看些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

相關文章
相關標籤/搜索