Backbone Events 源碼筆記

用了backbone一段時間了,作一些筆記和總結,看的源碼是1.12express

backbone有events,model,collection,histoty,router,view這些模塊,其中events是最基礎的,其餘的模塊的prototype所有都擴展了他,因此events是很是重要的,真的很重要,還好代碼比較簡單,也比較好理解數組

 

這個裏面的代碼是從backbone裏面剝離出來,而後一點一點研究和調試出來的,能夠單獨運行,依賴underscoreapp

  1     (function(){
  2         this.Backbone = {};
  3         var array = [];
  4         var slice = array.slice;
  5         // Regular expression used to split event strings.
  6         //劈開eventsApi函數裏面傳入name,若是name是帶空格的字符串
  7         var eventSplitter = /\s+/;
  8 
  9   // Implement fancy features of the Events API such as multiple event
 10   // names `"change blur"` and jQuery-style event maps `{change: action}`
 11   // in terms of the existing API.
 12         //若是傳入的name(這個對應綁定 刪除 觸發 監聽的事件名)爲obj 或者是帶空格的字符串,則批量進行相關的操做
 13         var eventsApi = function(obj, action, name, rest) {
 14             if (!name) return true;
 15 
 16             // Handle event maps.
 17             if (typeof name === 'object') {
 18                 for (var key in name) {
 19                     obj[action].apply(obj, [key, name[key]].concat(rest));
 20                 }
 21                 return false;
 22             }
 23 
 24             // Handle space separated event names.
 25             if (eventSplitter.test(name)) {
 26                 var names = name.split(eventSplitter);
 27                 for (var i = 0, length = names.length; i < length; i++) {
 28                     obj[action].apply(obj, [names[i]].concat(rest));
 29                 }
 30                 return false;
 31             }
 32 
 33             return true;
 34         };
 35 
 36 
 37 
 38         var Events = Backbone.Events = {
 39             // Bind an event to a `callback` function. Passing `"all"` will bind
 40             // the callback to all events fired.
 41             // 參數的傳入爲  事件名, 回調, 回調裏面this指向的對象
 42             on: function(name, callback, context) {
 43                 if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
 44                 //對象內部生成一個_events對象 key對應事件名, value爲一個數組,裏面添加相關的回調函數
 45                 this._events || (this._events = {});
 46                 var events = this._events[name] || (this._events[name] = []);
 47                 //第3個和第4個參數,表示回調函數觸發的是偶, 函數裏面this的指向
 48                 events.push({callback: callback, context: context, ctx: context || this});
 49                 return this;
 50             },
 51             // Bind an event to only be triggered a single time. After the first time
 52             // the callback is invoked, it will be removed.
 53             //  跟on同樣的入參 ,他只會執行一次
 54             once: function(name, callback, context) {
 55                 if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
 56                 var self = this;
 57                 //真正綁定進_.events的事故once 而不是callback,當once執行完一次後,就從_events上面刪除掉了
 58                 var once = _.once(function() {
 59                     self.off(name, once);
 60                     callback.apply(this, arguments);
 61                 });
 62                 once._callback = callback;
 63                 return this.on(name, once, context);
 64             },
 65 
 66             // Trigger one or many events, firing all bound callbacks. Callbacks are
 67             // passed the same arguments as `trigger` is, apart from the event name
 68             // (unless you're listening on `"all"`, which will cause your callback to
 69             // receive the true name of the event as the first argument).
 70             trigger: function(name) {
 71                 if (!this._events) return this;
 72                 var args = slice.call(arguments, 1);
 73                 if (!eventsApi(this, 'trigger', name, args)) return this;
 74                 var events = this._events[name];
 75                 var allEvents = this._events.all;
 76                 //經過name 找到對應的回調數組  依次執行裏面的回調
 77                 if (events) triggerEvents(events, args);
 78                 //查看是否綁定了all事件 若是綁定也會出阿發
 79                 if (allEvents) triggerEvents(allEvents, arguments);
 80                 return this;
 81             },
 82 
 83             // Remove one or many callbacks. If `context` is null, removes all
 84             // callbacks with that function. If `callback` is null, removes all
 85             // callbacks for the event. If `name` is null, removes all bound
 86             // callbacks for all events.
 87             off: function(name, callback, context) {
 88                 if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
 89 
 90                 // Remove all callbacks for all events.
 91                 //若是參數不存在 就移除全部的事件
 92                 if (!name && !callback && !context) {
 93                     this._events = void 0;
 94                     return this;
 95                 }
 96 
 97                 var names = name ? [name] : _.keys(this._events);
 98                 for (var i = 0, length = names.length; i < length; i++) {
 99                     name = names[i];
100 
101                     // Bail out if there are no events stored.
102                     var events = this._events[name];
103                     if (!events) continue;
104 
105                     // Remove all callbacks for this event.
106                     //若是隻傳遞name這一個參數 就刪除name對應的整個數組
107                     if (!callback && !context) {
108                         delete this._events[name];
109                         continue;
110                     }
111 
112                     // Find any remaining events.
113                     //
114                     //若是若是傳遞了第2個參數  只刪除_events[name]裏面對應的callback
115                     //若是傳遞了第2個參數,第3個參數,還要判斷_events[name]裏面的callback是否等於第2個參數,context是否等於第3個參數
116                     //把那麼不符合條件的用remaining保存起來
117                     //而後用this._events[name] = remaining替換掉以前的
118                     var remaining = [];
119                     for (var j = 0, k = events.length; j < k; j++) {
120                         var event = events[j];
121                         if (
122                             callback && callback !== event.callback &&
123                             callback !== event.callback._callback ||
124                             context && context !== event.context
125                         ) {
126                             remaining.push(event);
127                         }
128                     }
129 
130                     // Replace events if there are any remaining.  Otherwise, clean up.
131                     if (remaining.length) {
132                         this._events[name] = remaining;
133                     } else {
134                         delete this._events[name];
135                     }
136                 }
137 
138               return this;
139             },
140             // Tell this object to stop listening to either specific events ... or
141             // to every object it's currently listening to.
142             stopListening: function(obj, name, callback) {
143                 var listeningTo = this._listeningTo;
144                 if (!listeningTo) return this;
145                 var remove = !name && !callback;
146                 if (!callback && typeof name === 'object') callback = this;
147                 if (obj) (listeningTo = {})[obj._listenId] = obj;
148                 for (var id in listeningTo) {
149                     obj = listeningTo[id];
150                     obj.off(name, callback, this);
151                     if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
152                 }
153                 return this;
154             }            
155         }
156 
157         var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
158 
159         // Inversion-of-control versions of `on` and `once`. Tell *this* object to
160         // listen to an event in another object ... keeping track of what it's
161         // listening to.
162         //                                   on   listenTo
163         _.each(listenMethods, function(implementation, method) {
164             Events[method] = function(obj, name, callback) {
165                 //obj  爲被監聽的對象  
166                 //name 爲被監聽的事件名
167                 // callback 是obj觸發了name事件後, 別監聽到了 而後執行的回調                
168                 var listeningTo = this._listeningTo || (this._listeningTo = {});
169                 var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
170                 //給監聽對象添加一個_listeningTo的屬性 它的值是一個對象key爲_listenId value爲被監聽的對象  
171                 //被監聽對象添加一個_listenId的屬性    它的值爲_listenId
172                 listeningTo[id] = obj;
173 
174                 if (!callback && typeof name === 'object') callback = this;
175                 obj[implementation](name, callback, this);
176                 return this;
177             };
178         });        
179 
180         // A difficult-to-believe, but optimized internal dispatch function for
181         // triggering events. Tries to keep the usual cases speedy (most internal
182         // Backbone events have 3 arguments).
183         //批量處理_events裏面的回調事件的東西
184         //參數小於等於3個用call 大於3個用apply  爲毛會這樣
185         var triggerEvents = function(events, args) {
186             var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
187             switch (args.length) {
188                 case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
189                 case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
190                 case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
191                 case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
192                 default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
193             }
194         };        
195     })();
View Code

 

Backbone.Eventsless

Events主要就是對object進行綁定,觸發,刪除,監聽,它有如下一些方法ide

on bind 綁定事件函數

trigger 觸發綁定事件this

off unbind 移除綁定事件spa

listenTo 監聽對象prototype

stopListening 中止監聽調試

 

on    object.on(event, callback, [context])

綁定callback函數到object對象。 當事件觸發時執行回調函數callback。第一個參數是事件名,第2個參數是綁定的事件  第3個參數是一個對象,callback裏面若是有this,則this指向第3個參數,若是沒有傳遞第3個參數則callback裏面的this指向object自己

當綁定上一個事件的時候,該object會產生一個_events的屬性,該屬性是一個object類型,key對應的是事件名,value對應的是一個數組,數組裏面放的是全部的回調事件

當觸發一個事件的時候,它會遍歷_.events,根據傳遞的事件名,查找相關的回調事件,而後執行。若是綁定了all,則只要調用了trigger,就會調用all下面的全部的回調

一些例子

一個object能夠綁定多個事件,同一個事件能夠有多個回調

            var test = {};
            _.extend(test,Backbone.Events);
            test.on("a",function(){alert(1)}); 
            test.on("a",function(){alert(11)});
            test.on("b",function(){alert(2)});
            test.trigger("a");
            test.trigger("b");
       //test綁定了2個事件a,b  a綁定了2個回調函數

 

obj能夠批量綁定事件,就是第一個參數是個對象

            var obj = {c:1}
            var test={};
            _.extend(test,Backbone.Events);
            test.on({
                "a" : function(){alert(1111)},
                "b" : function(){alert(this.c)}
            },obj);
            test.trigger("b") //彈出1
            //能夠一次綁定2個事件a,b 傳入的第3個參數是個obj,因此回調執行的時候this指向obj

 

若是指定綁定事件名爲all,觸發任何綁定事件的時候都會觸發該事件

            var test = {};
            _.extend(test,Backbone.Events);
            test.on("a",function(){alert(1)});
            test.on("all",function(){alert(111)});
            test.trigger("a"); 
            //觸發a事件  也會自動觸發all事件

 

once    object.on(event, callback, [context])

綁定callback函數到object對象。 當事件觸發時執行回調函數callback。 第3個參數是一個對象,callback裏面若是有this,則this指向第3個參數。

該綁定事件只能觸發一次,而後就會被移除掉

            var test = {};
            _.extend(test,Backbone.Events);  
            test.once("a",function(){alert(1)});
            console.log(test);  //test._events裏面是有a屬性的
            test.trigger("a");
            console.log(test)  //test._events裏面的a屬性就被刪除掉了
            test.trigger("a"); 

 

trigger    object.trigger(event, [*args])

經過事件名觸發對應的回調函數。events後面的參數做爲參數傳入回調事件裏面

            var test = {};
            _.extend(test,Backbone.Events);  
            test.on("a",function(a){alert(a)});
            test.on("a",function(a,b){alert(a+b)}); 
            test.on("a",function(a,b,c){alert(a+b+c)});
            test.trigger("a",1,2,3) 
            //依次彈出1,3,6

 

off    object.off([event], [callback], [context])

從object中刪除之前綁定的回調函數

第一個參數是事件名

第2個參數是指定的函數,若是傳了,就只刪除綁定在object上的該函數,若是沒傳,則移除全部的函數

第3個參數是指定對象,若是綁定的時候調用回調指定了對象,刪除的時候也要把該對象帶上

 

若是調用off的時候不傳遞參數,則刪除全部的綁定事件

var test = {};
_.extend(test,Backbone.Events);  
test.on("a",function(){alert(1)})
test.on("b",function(){alert(1)})
console.log(test._events)
test.off()
console.log(test._events)
//刪除後test._events就是空的了

 

若是指定了刪除的事件名,而且傳遞了第2個參數,並且第2個參數是就是傳入的回調的事件,則只刪除該指定了回調函數

var test = {};
_.extend(test,Backbone.Events); 
var aFun = function(){alert(1)}
test.on("a",aFun )
test.on("a",function(){alert(1)})
console.log(test._events);
test.off("a",aFun)
console.log(test._events);
//只刪除了第二個參數指定的回調函數

 

listenTo    object.listenTo(other, event, callback)

object監聽other對象上的指定的方法名上(好比a),若是other觸發a,則也會觸發監聽的回調(callback)

當一個對象監聽其餘對象的事件名的時候(好比a,b,2個對象,b綁定haha事件,a監聽b的haha事件),a對象會生成_listeningTo的對象,根據_.uniqueId("l")生成的值來作key,value則是b這個對象了。b對象則會生成一個屬性_listenId,他的值就是前面_.uniqueId("l")生成的值, 而後若是傳入了監聽事件的回調函數 再b._events.haha的回調數組中加入該函數,當b觸發haha的時候也就會觸發該函數了。

listenTo仍是很重要的,好比一個view,初始化的時候就去listento一個model,當model,的時候就會自動通知view去渲染頁面

第一個參數是要監聽的對象,

第二個參數是須要監聽的事件名

第三個參數是當被監聽者處罰了該事件,執行的回調

var test = {};
var other = {};
_.extend(test,Backbone.Events);
_.extend(other,Backbone.Events); 
other.on("a",function(){alert(1)})
other.on("a",function(){alert(2)})
test.listenTo(other,"a",function(){alert("test")})
other.trigger("a")
//test監聽other,當other觸發a時,先執行完ohter本身綁定的回調,在執行監聽回調
相關文章
相關標籤/搜索