【原创】backbone1.1.0源码解析之Events
2014-01-01 23:56
429 查看
最近在看些node的源代码,发现backbone的应用还是挺广泛的,但是之前的学习忘得一干二净了,后悔当时没做笔记啊。
所以,无奈想用的更好,就是得把源代码看楚,所以还是把源代码的注释笔记留下来,供自己备忘,也供新手学习。
首先这里是Backbone.Events的实现代码注释,这是backbone的实现mvc的主要保障之一,这种基于事件的机制无论是在前端还是在后端nodejs部分
都有着很广的作用,更何况nodejs的异步机制更需要它来处理一些逻辑问题(当然有人会说promise会比你这种pubsub更适合异步,哈,我只是像表明事件机制的
重要性嘛)
下面是对代码的注释,如果有错误还望指出来
各位晚安~~
所以,无奈想用的更好,就是得把源代码看楚,所以还是把源代码的注释笔记留下来,供自己备忘,也供新手学习。
首先这里是Backbone.Events的实现代码注释,这是backbone的实现mvc的主要保障之一,这种基于事件的机制无论是在前端还是在后端nodejs部分
都有着很广的作用,更何况nodejs的异步机制更需要它来处理一些逻辑问题(当然有人会说promise会比你这种pubsub更适合异步,哈,我只是像表明事件机制的
重要性嘛)
下面是对代码的注释,如果有错误还望指出来
// Backbone.Events // --------------- // A module that can be mixed in to *any object* in order to provide it with // custom events. You may bind with `on` or remove with `off` callback // functions to an event; `trigger`-ing an event fires all callbacks in // succession. // // var object = {}; // _.extend(object, Backbone.Events); // object.on('expand', function(){ alert('expanded'); }); // object.trigger('expand'); // var Events = Backbone.Events = { // Bind an event to a `callback` function. Passing `"all"` will bind // the callback to all events fired. on: function(name, callback, context) { // 两种情况: // 1. 一次添加多个事件时,通过eventsApi一个一个添加,所以eventsApi返回false,那么直接return // 2. 回调函数没定义,没有意义,直接return if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; // 因此这里往下事件是一个一个添加的,即name是一个事件名(如:click | custom) // 初始化私有变量,用于存储事件的信息 this._events || (this._events = {}); var events = this._events[name] || (this._events[name] = []); // 这里可以写出events的结构了 // _events : { // click : [{ // callback : cb1, // context : ctx1, // ctx : ctx1 || this // },{ // callback : cb2, // context : ctx2, // ctx : ctx2 || this // }, ...], // blur : [{...}, {...}, ...], // ... // } events.push({callback: callback, context: context, ctx: context || this}); return this; }, // Bind an event to only be triggered a single time. After the first time // the callback is invoked, it will be removed. once: function(name, callback, context) { if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; var self = this; // 将callback进行包装,返回的新函数newCallback内部会调用calllback, // 不同的是,newCallback只会调用callback一次,之后只会返回callback执行的结果 // 也就是说once实质上并没有去除掉事件监听函数,而是控制了callback只会执行一次 var once = _.once(function() { self.off(name, once); callback.apply(this, arguments); }); // 保留原callback,用于off操作 once._callback = callback; // 实质调用.on()方法注册事件 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; // 两种情况: // 1. 根本没注册过事件,何谈删除事件,直接return // 2. 像上述所说支持多事件删除 if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; // 如果是obj.off()这样调用,那么删除该对象obj上所有的事件监听 // 也就是是将_events清空了 if (!name && !callback && !context) { this._events = {}; return this; } // 如果name为空,像obj.off(undefined, cb1, ctx1) // 那么name就为所有注册过的事件(即_.keys(this._events)) names = name ? [name] : _.keys(this._events); // 根据name遍历events for (i = 0, l = names.length; i < l; i++) { name = names[i]; if (events = this._events[name]) { this._events[name] = retain = []; // 如果callback或者context有一个有值 // 那么接下来将它们作为条件进行接下来事件的off操作 // 实质其实是先清空_events,将不满足条件删除条件的事件监听器重新填入_events中 if (callback || context) { for (j = 0, k = events.length; j < k; j++) { ev = events[j]; // 这里对指定了callback或者context的情况,做了条件判断 // 这里的_callback是因为.once方法会对原callback进行包装,这里的evn.callback就是包装后的,原callback保存在_callback中 if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || (context && context !== ev.context)) { retain.push(ev); } } } // 发现该事件的事件监听器被删光了,那么做了清理工作,删除_events对应的key 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; // 分离出传给callback的参数 var args = slice.call(arguments, 1); // 同样支持多事件同时trigger if (!eventsApi(this, 'trigger', name, args)) return this; // 该对象该事件的信息 var events = this._events[name]; // 该对象all事件的信息(all事件是一个特殊的事件,all事件在其他事件发生时都会被触发) var allEvents = this._events.all; // 下面两部分别是触发当前事件的回调以及all事件的回调 // 不同的是,all事件的回调会被多传递一个触发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 listeningTo = this._listeningTo; if (!listeningTo) return this; // var remove = !name && !callback; // 这里是兼容(obj, {click: cb1, change: cb2})这种形式 // 保证第三个参数是作为context传入,这里是this if (!callback && typeof name === 'object') callback = this; // 如果有指定obj,那么解除对象只针对obj // 如果没有指定,则是解除监听的所有对象的事件绑定 if (obj) (listeningTo = {})[obj._listenId] = obj; for (var id in listeningTo) { obj = listeningTo[id]; obj.off(name, callback, this); // 两种情况下做清理工作 // 1. 已经表明清除对obj的的所有事件监听(即name和callback都为空) // 2. obj对象自身都没有被绑定事件了,哪来的事件让你监听呢? if (remove || _.isEmpty(obj._events)) delete this._listeningTo[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. // ({}, 'on', 'click blur', [function () {}, undefined]) var eventsApi = function(obj, action, name, rest) { if (!name) return true; // Handle event maps. // 支持映射关系 // 如:(obj, 'on', {'click': function x () {}, 'blur': function xx () {}}, context) if (typeof name === 'object') { for (var key in name) { // 反复调用action(on | off | once), 每次添加一个事件监听,从而达到添加多个。 obj[action].apply(obj, [key, name[key]].concat(rest)); } return false; } // Handle space separated event names. // 支持空格分割事件(即多事件共享同一个函数) // 如:(obj, 'on', 'click blur', function () {}, context) 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). // 这里做了个优化,就是如果arg参数在3个之类的话,用call进行调用, // 因为call要比apply的效率高(http://jsperf.com/function-versus-function-call-versus-function-apply) 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. // 添加listenTo和listernToOnce方法 // 实质是: // 1. 给需要监听的对象obj赋予一个_listenId的随机id // 2. 再给监听者(调用对象)添加一个map就是listeningTo属性,添加上述的id和obj // 3. 给obj绑定被监听的事件被将this指向调用者 // 这里实质就是调用obj的on或者once方法来添加事件监听, // 那么单独列出这样的一个方法的好处在于方便监听者,可以随时监听和解除监听,上述的1,2两不操作是为了以后解除监听做准备 _.each(listenMethods, function(implementation, method) { Events[method] = function(obj, name, callback) { var listeningTo = this._listeningTo || (this._listeningTo = {}); var id = obj._listenId || (obj._listenId = _.uniqueId('l')); listeningTo[id] = obj; // 这里是兼容(obj, {click: cb1, change: cb2})这种形式 // 保证第三个参数是作为context传入,这里是this if (!callback && 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. // 将Backbone对像拥有事件绑定机制 _.extend(Backbone, Events);
各位晚安~~
相关文章推荐
- 【原创】backbone1.1.0源码解析之Collection
- 【原创】backbone1.1.0源码解析之View
- 【原创】backbone1.1.0源码解析之Model
- 【原创】backbone0.9.2源码解析之extend
- Backbone源码解析(三):Collection模块
- 前端MVC框架Backbone 1.1.0源码分析系列
- [原创]基于NTFS磁盘格式的解析/删除/读写破坏.完整源码
- C语言解析pcap文件得到HTTP信息实例(原创,附源码)
- 前端MVC框架Backbone 1.1.0源码分析(二) - 模型
- 【原创】jQuery1.8.2源码解析之jQuery.Callbacks
- 【原创】jQuery1.8.2源码解析之jQuery.Deferred
- Backbone源码解析(六):观察者模式应用
- Nodejs源码解析之events
- 《stlport》源码解析-目录篇 @原创@
- Backbone源码解析(四):View(视图)模块
- C语言解析pcap文件得到HTTP信息实例(原创,附源码)
- 【原创】jQuery1.8.2源码解析之jQuery结构图
- LINUX编程专题-I/O复用:epoll原理和源码解析(资料的收集和整理,不算原创)
- Backbone源码解析系列
- 【原创】jQuery1.8.2源码解析之jQuery.event