最近在看些node的源代码,发现backbone的应用还是挺广泛的,但是之前的学习忘得一干二净了,后悔当时没做笔记啊。

所以,无奈想用的更好,就是得把源代码看楚,所以还是把源代码的注释笔记留下来,供自己备忘,也供新手学习。

首先这里是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源码解析之Events的更多相关文章

  1. 【原创】backbone1.1.0源码解析之View

    作为MVC框架,M(odel)  V(iew)  C(ontroler)之间的联系是必不可少的,今天要说的就是View(视图) 通常我们在写逻辑代码也好或者是在ui组件也好,都需要跟dom打交道,我们 ...

  2. 【原创】backbone1.1.0源码解析之Collection

    晚上躺在床上,继续完成对Backbone.Collection的源码解析. 首先讲讲它用来干嘛? Backbone.Collection的实例表示一个集合,是很多model组成的,如果用model比喻 ...

  3. 【原创】backbone1.1.0源码解析之Model

    趁热打铁,将Backbone.Model的源代码注释也发出来. Model是用来干嘛的?写过mvc的同学应该都知道,说白了就是model实例用来存储数据表中的一行数据(row) Backbone利用m ...

  4. solr&lucene3.6.0源码解析(四)

    本文要描述的是solr的查询插件,该查询插件目的用于生成Lucene的查询Query,类似于查询条件表达式,与solr查询插件相关UML类图如下: 如果我们强行将上面的类图纳入某种设计模式语言的话,本 ...

  5. solr&lucene3.6.0源码解析(三)

    solr索引操作(包括新增 更新 删除 提交 合并等)相关UML图如下 从上面的类图我们可以发现,其中体现了工厂方法模式及责任链模式的运用 UpdateRequestProcessor相当于责任链模式 ...

  6. Heritrix 3.1.0 源码解析(三十七)

    今天有兴趣重新看了一下heritrix3.1.0系统里面的线程池源码,heritrix系统没有采用java的cocurrency包里面的并发框架,而是采用了线程组ThreadGroup类来实现线程池的 ...

  7. solr&lucene3.6.0源码解析(二)

    上文描述了solr3.6.0怎么采用maven管理的方式在eclipse中搭建开发环境,在solr中,为了提高搜索性能,采用了缓存机制,这里描述的是LRU缓存,这里用到了 LinkedHashMap类 ...

  8. solr&lucene3.6.0源码解析(一)

      本文作为系列的第一篇,主要描述的是solr3.6.0开发环境的搭建   首先我们需要从官方网站下载solr的相关文件,下载地址为http://archive.apache.org/dist/luc ...

  9. apache mina2.0源码解析(一)

    apache mina是一个基于java nio的网络通信框架,为TCP UDP ARP等协议提供了一致的编程模型:其源码结构展示了优秀的设计案例,可以为我们的编程事业提供参考. 依照惯例,首先搭建a ...

随机推荐

  1. Centos 7 安装mysql5.7.24二进制 版本

    Mysql 二进制安装方法 下载mysql https://dev.mysql.com/downloads/mysql/ 1.解压包 tar xf mysql-5.7.24-linux-glibc2. ...

  2. IDEA2018.1安装配置文档

    一.软件安装 1. 下载地址: https://www.jetbrains.com/idea/download/#section=windows 2. 安装: 点击.exe,选择安装路径,点击next ...

  3. Java serialVersionUID作用和生成

    序列化和反序列化Java是面向对象的语言,与其他语言进行交互(比如与前端js进行http通信),需要把对象转化成一种通用的格式比如json(前端显然不认识Java对象),从对象到json字符串的转换, ...

  4. 第二阶段站立会议alpha版总结

    一.会议过程 在完成第二次冲刺后,Alpha版本最终发布,我们对校园二手交易平台开发过程及产品存在的问题进行了激烈讨论.进行了我们的团队总结会议,会议中每个人先发表了个人对Alpha版开发过程中存在的 ...

  5. Eat Style --proposed by Chongyang Bai

    NEED 1. 有人希望妈妈是这样的: 但实际上对妈妈做的菜反应确是这样的: 处在不同的时节,根据不同的个人偏好,到底该做些什么饭菜?工作繁忙,家里的厨师可能也没时间琢磨.最后做出的只是应付差事的饭菜 ...

  6. tp5+layui 实现上传大文件

    前言: 之前所写的文件上传类通常进行考虑的是文件的类型.大小是否符合要求条件.当上传大文件时就要考虑到php的配置和服务器的配置问题.之前简单的觉得只要将php.ini中的表单上传的 大小,单脚本执行 ...

  7. PAT 1044 火星数字

    https://pintia.cn/problem-sets/994805260223102976/problems/994805279328157696 火星人是以13进制计数的: 地球人的0被火星 ...

  8. [转帖]SAP MES生产执行系统解决方案

    一.SAP MES概述: SAP公司成立于1972年,总部位于德国,是全球最大的企业管理和协同化商务解决方案供应商.全球第三大独立软件供应商.目前,在全球有120多个国家的超过86,000多家用户正在 ...

  9. shell脚本实现多台服务器自动巡检

    shell脚本实现多台服务器自动巡检 摘要:         运维服务一个项目二十多台(或者多台)服务器,每天要做服务器的性能巡检工作是查看服务器的CPU.内存.磁盘空间是否在正常值范围内.像这样每天 ...

  10. LVS(Linus Virtual Server):三种负载均衡方式比较+另三种负载均衡方式

    还有个姊妹篇也可以参考这个文章:六大Web负载均衡原理与实现 什么是LVS (Linux Virtual Server)?   首先简单介绍一下LVS (Linux Virtual Server)到底 ...