Backbone.js 1.0.0源码架构分析(二)——Event
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ( function (){ //省略前面代码 var Events = Backbone.Events = { // 根据name订阅事件,push到this._events[name] on: function (name, callback, context) {<br> //如果name为key/value map形式(对象)或空格间隔的字符串,那么对里面的key或元素分别遍历处理(即对子元素调用on方法,根据name订阅事件)<br> if (!eventsApi( this , 'on' , name, [callback, context]) || !callback) return this ;<br> // 事件集合 this._events this ._events || ( this ._events = {}); var events = this ._events[name] || ( this ._events[name] = []); events.push({callback: callback, context: context, ctx: context || this }); return this ; }, // 实现方法同上,只不过实现一次之后就会被销毁 once: function (name, callback, context) { if (!eventsApi( this , 'once' , name, [callback, context]) || !callback) return this ; var self = this ; var once = _.once( function () { self.off(name, once); callback.apply( this , arguments); }); once._callback = callback; return this .on(name, once, context); }, // Remove one or many callbacks. If `context` is null, removes all // callbacks with that function. If `callback` is null, removes all // callbacks for the event. If `name` is null, removes all bound // callbacks for all events. off: function (name, callback, context) { var retain, ev, events, names, i, l, j, k; if (! this ._events || !eventsApi( this , 'off' , name, [callback, context])) return this ; if (!name && !callback && !context) { this ._events = {}; return this ; } // name不存在,获取this._events内所有的key names = name ? [name] : _.keys( this ._events); for (i = 0, l = names.length; i < l; i++) { name = names[i]; if (events = this ._events[name]) { this ._events[name] = retain = []; //空数组 //callback 或者 context 存在<br> // callback 存在,遍历this._events[name],找出callback !== ev.callback && callback !== ev.callback._callback的元素删除,总感觉这逻辑有点怪<br> // context存在,方法同上 if (callback || context) { for (j = 0, k = events.length; j < k; j++) { ev = events[j]; if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || (context && context !== ev.context)) { retain.push(ev); } } }<br> //如果有不同于参数callback的ev,则删除this._events[name]!查阅官方issue得知,无论callback或context是否存在,都会删除this._events[name] if (!retain.length) delete this ._events[name]; } } return this ; }, // Trigger one or many events, firing all bound callbacks. Callbacks are // passed the same arguments as `trigger` is, apart from the event name // (unless you're listening on `"all"`, which will cause your callback to // receive the true name of the event as the first argument). trigger: function (name) { if (! this ._events) return this ; var args = slice.call(arguments, 1); if (!eventsApi( this , 'trigger ', name, args)) return this; var events = this._events[name]; var allEvents = this._events.all; if (events) triggerEvents(events, args); if (allEvents) triggerEvents(allEvents, arguments); return this; }, // Tell this object to stop listening to either specific events ... or // to every object it' s currently listening to. stopListening: function (obj, name, callback) { var listeners = this ._listeners; if (!listeners) return this ; var deleteListener = !name && !callback; if ( typeof name === 'object' ) callback = this ; if (obj) (listeners = {})[obj._listenerId] = obj; for ( var id in listeners) { listeners[id].off(name, callback, this ); if (deleteListener) delete this ._listeners[id]; } return this ; } }; // Regular expression used to split event strings. var eventSplitter = /\s+/; // Implement fancy features of the Events API such as multiple event // names `"change blur"` and jQuery-style event maps `{change: action}` // in terms of the existing API. var eventsApi = function (obj, action, name, rest) { if (!name) return true ; // Handle event maps.分别处理每个key/value,形成递归 if ( typeof name === 'object' ) { for ( var key in name) { obj[action].apply(obj, [key, name[key]].concat(rest)); } return false ; } // Handle space separated event names.分别处理每个元素,形成递归 if (eventSplitter.test(name)) { var names = name.split(eventSplitter); for ( var i = 0, l = names.length; i < l; i++) { obj[action].apply(obj, [names[i]].concat(rest)); } return false ; } return true ; }; // A difficult-to-believe, but optimized internal dispatch function for // triggering events. Tries to keep the usual cases speedy (most internal // Backbone events have 3 arguments). 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); } }; var listenMethods = {listenTo: 'on' , listenToOnce: 'once' }; // Inversion-of-control versions of `on` and `once`. Tell *this* object to // listen to an event in another object ... keeping track of what it's // listening to. _.each(listenMethods, function (implementation, method) { Events[method] = function (obj, name, callback) { var listeners = this ._listeners || ( this ._listeners = {}); var id = obj._listenerId || (obj._listenerId = _.uniqueId('l ')); listeners[id] = obj; if (typeof name === ' object') callback = this ; obj[implementation](name, callback, this ); return this ; }; }); // Aliases for backwards compatibility. Events.bind = Events.on; Events.unbind = Events.off; // Allow the `Backbone` object to serve as a global event bus, for folks who // want global "pubsub" in a convenient place. _.extend(Backbone, Events); //省略后面代码 }).call( this ) |
让我们来梳理一下backbone.js中Events的实现思路:
1 | Backbone.Events可以实现订阅事件(on或once方法)即把Model中某个属性值加入 this ._events集合中,如果该属性值发生变化,就触发此定义事件的callback(trigger方法);如果不需要该订阅,通过off方法取消订阅。可以看成Backbone.Events是pub/sub的模式 |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步