Backbone Events 源码笔记
用了backbone一段时间了,做一些笔记和总结,看的源码是1.12
backbone有events,model,collection,histoty,router,view这些模块,其中events是最基础的,其他的模块的prototype全部都扩展了他,所以events是非常重要的,真的很重要,还好代码比较简单,也比较好理解
这个里面的代码是从backbone里面剥离出来,然后一点一点研究和调试出来的,可以单独运行,依赖underscore
(function(){
this.Backbone = {};
var array = [];
var slice = array.slice;
// Regular expression used to split event strings.
//劈开eventsApi函数里面传入name,如果name是带空格的字符串
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.
//如果传入的name(这个对应绑定 删除 触发 监听的事件名)为obj 或者是带空格的字符串,则批量进行相关的操作
var eventsApi = function(obj, action, name, rest) {
if (!name) return true;
// Handle event maps.
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, length = names.length; i < length; i++) {
obj[action].apply(obj, [names[i]].concat(rest));
}
return false;
}
return true;
};
var Events = Backbone.Events = {
// Bind an event to a `callback` function. Passing `"all"` will bind
// the callback to all events fired.
// 参数的传入为 事件名, 回调, 回调里面this指向的对象
on: function(name, callback, context) {
if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
//对象内部生成一个_events对象 key对应事件名, value为一个数组,里面添加相关的回调函数
this._events || (this._events = {});
var events = this._events[name] || (this._events[name] = []);
//第3个和第4个参数,表示回调函数触发的是偶, 函数里面this的指向
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.
// 跟on一样的入参 ,他只会执行一次
once: function(name, callback, context) {
if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
var self = this;
//真正绑定进_.events的事故once 而不是callback,当once执行完一次后,就从_events上面删除掉了
var once = _.once(function() {
self.off(name, once);
callback.apply(this, arguments);
});
once._callback = callback;
return this.on(name, once, context);
},
// 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;
//通过name 找到对应的回调数组 依次执行里面的回调
if (events) triggerEvents(events, args);
//查看是否绑定了all事件 如果绑定也会出阿发
if (allEvents) triggerEvents(allEvents, arguments);
return this;
},
// 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) {
if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
// Remove all callbacks for all events.
//如果参数不存在 就移除所有的事件
if (!name && !callback && !context) {
this._events = void 0;
return this;
}
var names = name ? [name] : _.keys(this._events);
for (var i = 0, length = names.length; i < length; i++) {
name = names[i];
// Bail out if there are no events stored.
var events = this._events[name];
if (!events) continue;
// Remove all callbacks for this event.
//如果只传递name这一个参数 就删除name对应的整个数组
if (!callback && !context) {
delete this._events[name];
continue;
}
// Find any remaining events.
//
//如果如果传递了第2个参数 只删除_events[name]里面对应的callback
//如果传递了第2个参数,第3个参数,还要判断_events[name]里面的callback是否等于第2个参数,context是否等于第3个参数
//把那么不符合条件的用remaining保存起来
//然后用this._events[name] = remaining替换掉之前的
var remaining = [];
for (var j = 0, k = events.length; j < k; j++) {
var event = events[j];
if (
callback && callback !== event.callback &&
callback !== event.callback._callback ||
context && context !== event.context
) {
remaining.push(event);
}
}
// Replace events if there are any remaining. Otherwise, clean up.
if (remaining.length) {
this._events[name] = remaining;
} else {
delete this._events[name];
}
}
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;
if (!callback && typeof name === 'object') callback = this;
if (obj) (listeningTo = {})[obj._listenId] = obj;
for (var id in listeningTo) {
obj = listeningTo[id];
obj.off(name, callback, this);
if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
}
return this;
}
}
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.
// on listenTo
_.each(listenMethods, function(implementation, method) {
Events[method] = function(obj, name, callback) {
//obj 为被监听的对象
//name 为被监听的事件名
// callback 是obj触发了name事件后, 别监听到了 然后执行的回调
var listeningTo = this._listeningTo || (this._listeningTo = {});
var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
//给监听对象添加一个_listeningTo的属性 它的值是一个对象key为_listenId value为被监听的对象
//被监听对象添加一个_listenId的属性 它的值为_listenId
listeningTo[id] = obj;
if (!callback && typeof name === 'object') callback = this;
obj[implementation](name, callback, this);
return this;
};
});
// 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).
//批量处理_events里面的回调事件的东西
//参数小于等于3个用call 大于3个用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); return;
}
};
})();
Backbone.Events
Events主要就是对object进行绑定,触发,删除,监听,它有以下一些方法
on bind 绑定事件
trigger 触发绑定事件
off unbind 移除绑定事件
listenTo 监听对象
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自己绑定的回调,在执行监听回调
Backbone Events 源码笔记的更多相关文章
- backbone.Collection源码笔记
Backbone.Collection backbone的Collection(集合),用来存储多个model,并且可以多这些model进行数组一样的操作,比如添加,修改,删除,排序,插入,根据索引取 ...
- backbone.Model 源码笔记
backbone.Model backbone的model(模型),用来存储数据,交互数据,数据验证,在view里面可以直接监听model来达到model一改变,就通知视图. 这个里面的代码是从bac ...
- redis源码笔记(一) —— 从redis的启动到command的分发
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...
- Tomcat8源码笔记(三)Catalina加载过程
之前介绍过 Catalina加载过程是Bootstrap的load调用的 Tomcat8源码笔记(二)Bootstrap启动 按照Catalina的load过程,大致如下: 接下来一步步分析加载过程 ...
- Zepto源码笔记(一)
最近在研究Zepto的源码,这是第一篇分析,欢迎大家继续关注,第一次写源码笔记,希望大家多指点指点,第一篇文章由于首次分析原因不会有太多干货,希望后面的文章能成为各位大大心目中的干货. Zepto是一 ...
- AsyncTask源码笔记
AsyncTask源码笔记 AsyncTask在注释中建议只用来做短时间的异步操作,也就是只有几秒的操作:如果是长时间的操作,建议还是使用java.util.concurrent包中的工具类,例如Ex ...
- Java Arrays 源码 笔记
Arrays.java是Java中用来操作数组的类.使用这个工具类可以减少平常很多的工作量.了解其实现,可以避免一些错误的用法. 它提供的操作包括: 排序 sort 查找 binarySearch() ...
- Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目
以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...
- Tomcat8源码笔记(七)组件启动Server Service Engine Host启动
一.Tomcat启动的入口 Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误.Tomcat启动类是Bootstrap,而启动容器启动入口位于 ...
随机推荐
- HTML第七天学习笔记
今天主要是学习如何使用JS,第一个就是先是使用JS输出"Hello world" <!doctype html> <html lang="en" ...
- nodejs以及npm的安装
参考资料:http://xiaoyaojones.blog.163.com/blog/static/28370125201351501113581/ 上面的仁兄说的比较清楚,基本解决了安装中遇到的问题 ...
- 【转】Android 属性动画(Property Animation) 完全解析 (上)
http://blog.csdn.net/lmj623565791/article/details/38067475 1.概述 Android提供了几种动画类型:View Animation .Dra ...
- 解决ArcGIS Android Could not find class 'com.esri.android.map.MapView'问题
环境win7 64bit sp1,eclipse 4.2.1 ,android API 16,ADT 23.0.2,arcgis android sdk 10.2.4 从arcgis-android- ...
- windows下安装,配置gcc编译器
在Windows下使用gcc编译器: 1.首先介绍下MinGW MinGW是指仅仅用自由软件来生成纯粹的Win32可运行文件的编译环境,它是Minimalist GNU on Windows的略称. ...
- ssh 命令行通过私钥登录其它设备
ssh -i root(私钥文件) root@IP (被访问的服务器IP) 这里备份一下了
- Codeforces Round #307 (Div. 2) C. GukiZ hates Boxes 贪心/二分
C. GukiZ hates Boxes Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/551/ ...
- oc-17-description
Book.h #import <Foundation/Foundation.h> @interface Book : NSObject { NSString *_bookName; // ...
- 【iOS开发必备指南合集】申请企业级IDP、真机调试、游戏接入GameCenter 指南(实现仿官方的成就提示)、游戏接入OpenFeint指南;
本站文章均为李华明Himi原创,转载务必在明显处注明:(作者新浪微博:@李华明Himi) 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-c ...
- java字符串分解 StringTokenizer用法(比split()方法效率高)
Java中substring方法可以分解字符串,返回的是原字符串的一个子字符串.如果要讲一个字符串分解为一个一个的单词或者标记,StringTokenizer可以帮你. int countTokens ...