Backbone.js 1.0.0源码架构分析(二)——Event
(function(){
//省略前面代码 var Events = Backbone.Events = { // 根据name订阅事件,push到this._events[name]
on: function(name, callback, context) {
//如果name为key/value map形式(对象)或空格间隔的字符串,那么对里面的key或元素分别遍历处理(即对子元素调用on方法,根据name订阅事件)
if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
// 事件集合 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 存在
// callback 存在,遍历this._events[name],找出callback !== ev.callback && callback !== ev.callback._callback的元素删除,总感觉这逻辑有点怪
// 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);
}
}
}
//如果有不同于参数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的实现思路:
Backbone.Events可以实现订阅事件(on或once方法)即把Model中某个属性值加入this._events集合中,如果该属性值发生变化,就触发此定义事件的callback(trigger方法);如果不需要该订阅,通过off方法取消订阅。可以看成Backbone.Events是pub/sub的模式
Backbone.js 1.0.0源码架构分析(二)——Event的更多相关文章
- Backbone.js 1.0.0源码架构分析(一)
Backbone.js 是javascript 语言中 首个实现MVC设计模式的类库,API接口方法重度依赖于underscore.js,DOM选择器则依赖于jQuery.js或者zepto.js. ...
- 【NopCommerce源码架构学习-二】单例模式实现代码分析
单例模式是是常用经典十几种设计模式中最简单的..NET中单例模式的实现也有很多种方式.下面我来介绍一下NopCommerce中单例模式实现. 我之前的文章就分析了一下nop中EngineContext ...
- Spring5源码深度分析(二)之理解@Conditional,@Import注解
代码地址: 1.源码分析二主要分析的内容 1.使用@Condition多条件注册bean对象2.@Import注解快速注入第三方bean对象3.@EnableXXXX 开启原理4.基于ImportBe ...
- 如何快速为团队打造自己的组件库(上)—— Element 源码架构
文章已收录到 github,欢迎 Watch 和 Star. 简介 详细讲解了 ElementUI 的源码架构,为下一步基于 ElementUI 打造团队自己的组件库打好坚实的基础. 如何快速为团队打 ...
- 一起学习jQuery2.0.3源码—1.开篇
write less,do more jQuery告诉我们:牛逼的代码不仅精简而且高效! 2006年1月由美国人John Resig在纽约的barcamp发布了jQuery,吸引了来自世界各地众多Ja ...
- 深入解析Underscore.js源码架构
Underscore.js是很有名的一个工具库,我也经常用他来处理对象,数组等,本文会深入解析Underscore源码架构,跟大家一起学习下他源码的亮点,然后模仿他写一个简单的架子来加深理解.他的源码 ...
- jQuery 2.0.3 源码分析 Deferred概念
JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...
- jQuery 2.0.3 源码分析 Deferrred概念
转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而 ...
- Spark2.1.0之源码分析——事件总线
阅读提示:阅读本文前,最好先阅读<Spark2.1.0之源码分析——事件总线>.<Spark2.1.0事件总线分析——ListenerBus的继承体系>及<Spark2. ...
随机推荐
- 【DFS】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem D. Divisibility Game
题意:给你一个序列,长度不超过52,每个元素不超过13.让你重新对这个序列排序,sum(i)表示i的前缀和,使得排序过后,对每个i,都有sum(i)%i==0. 深搜,加两个优化:①倒着从后向前搜:② ...
- 【深度搜索+剪枝】POJ2362-Square
从昨天晚上写到现在,一直在TLE,现在终于剪枝完成了_(:зゝ∠)_ [思路] 深搜:用这类型组合题目最基本的深搜,变量side记录当成已经组成了几条变,sl表示当前在组合的边已经有的长度.如果当前s ...
- [HihoCoder1259]A Math Problem
题目大意: 有一个函数f(n),满足3f(n)*f(2n+1)=f(2n)*(1+3f(n)),f(2n)<6f(n). 我们用g(t)表示f(i)%k=t的i的个数,其中1<=i< ...
- iOS 在系统设置中展示Version, Build, Git等信息
在设置中,展示自定义内容,如下图INFO区域内容: 步骤: 1.在项目中添加Settings.bundle文件 Root.plist和Root.plist的Source code如下 ...
- 各种GCC
Cross GCC Cygwin GCC Linux GCC MacOSX GCC MinGW GCC Solaris GCC Clang
- [HTML/CSS]z-Index层重叠顺序
目录 语法结构 如何使用? 使用案例 总结 语法结构 z-index:具体数字.如:div{z-index:100},注意,后面跟的数字并不带单位.主要应用在div,span等标签的层叠处理. 如何使 ...
- 【转】Points To Line
原文地址 Python+Arcpy操作Points(.shp)转换至Polyline(.shp),仔细研读Points To Line (Data Management)说明,参数说明如下: Inpu ...
- 【Rocket MQ】RocketMQ4.2.0 和 spring boot的结合使用,实现分布式事务
RocketMQ4.2.0 和 spring boot的结合使用,实现分布式事务 参考地址:https://www.jianshu.com/p/f57de40621a0
- C#之Enum中的Flag
我们知道在默认情况下,第一个枚举数的值为0,后面每个枚举数的值一次加1. enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri}; 我们也可以用初始值来重写默认值. ...
- 配置安全证书的Etcd集群
不知在哪篇技术文档中看到,kubernetes master和etcd分开部署模式,因为集群的状态都保存在etcd中,这样当kubernetes master挂掉后,通过API Server交互的Sc ...