jquery源码解析:jQuery工具方法Callbacks详解
我们首先来讲下Callbacks是如何使用的:第一个例子
function a(){}
function b(){}
var cb = $.Callbacks();
cb.add(a);
cb.add(b);
cb.fire(); //就会先执行a方法,再执行b方法
上面大概的意思是:add方法,就是把方法添加到数组list中,每执行一次,就添加一个方法。而fire方法,就是把list数组中的方法按顺序执行。
使用场景:第二个例子
function a(n){ }
(function(){
function b(n){}
})
在这里你无法调用b方法,因为b方法是局部变量。因此,你可以使用Callbacks来解决这个问题。
function a(n){}
var cb = $.Callbacks();
cb.add(a);
(function(){
function b(n){}
cb.add(b);
})
cb.fire("hello");
这时,b方法即使是局部变量,也可以通过cb.fire()进行调用。而且fire中的参数"hello",也可以传入a,b方法。
Callbacks 可以传4个值,jQuery.Callbacks(options),options有四个值:once,memory,unique,stopOnFalse 。
在第一个例子中,假设我们调用两次cb.fire(); 那么第一次会执行a,b方法,第二次也会调用a,b方法。但是如果你定义cb时这样,var cb = $.Callbacks("once");那么只有第一次会执行a,b方法,第二次不会执行。once的意思是只执行list数组中的方法一次,之后你调用fire,不会执行list数组中的方法。
memory的作用是:
var cb = $.Callbacks();
cb.add(a);
cb.fire();
cb.add(b);
针对以上代码,方法a会执行,b方法不会执行。但是你在定义cb时,var cb = $.Callbacks("memory");这时,a,b方法都会执行。它具有记忆性。
unique的作用:去重,add如果添加相同的方法,在没有unique的情况下,添加几个相同的方法,就会执行几次相同的方法,但是你传入了unique,不管你添加多少次相同的方法,它都只会执行一次。
stopOnFalse的作用:
function a(){ return false;}
function b(){}
var cb = $.Callbacks();
cb.add(a);
cb.add(b);
cb.fire();
方法a,b都会执行。但是如果你定义cb时,var cb = $.Callbacks("stopOnFalse");只会执行a方法。因为a方法返回了false,而stopOnFalse的作用就是当方法返回false时,就停止循环list。因此b方法就不会执行了。
当然这四种参数,你可以组合使用,比如:var cb = $.Callbacks("once memory");
接下来,源码解析:由于代码比较多,因此给大家一个线索,$.Callbacks -> return self; -> self.add(a) -> list.push(a) -> self.add(b) -> list.push(b) -> self.fire() ->self.fireWith -> 私有方法fire() -> 循环list数组,执行里面的a,b方法。请按照此顺序看代码,然后再根据4个参数分别代表什么意思,再按顺序看一次。里面有详细的代码解释。
var optionsCache = {};
function createOptions( options ) {
var object = optionsCache[ options ] = {}; //optionsCache[once] = {}
jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
//core_rnotwhite = /\S+/g, 取字符,options.match( core_rnotwhite ) = ["once"],此正则是为了处理多个值时,比如"once match"变成["once","memory"]
object[ flag ] = true; //_是index值0,flag是value值"once"
});
return object; //optionsCache[once] = { "once":true }
}
jQuery.Callbacks = function( options ) { //options有四个值:once,memory,unique,stopOnFalse
options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options );
//当传入参数时,就是字符串,比如:once,那么就会先去optionsCache中取once属性,如果之前没有此值,就调用createOptions(once)方法
var memory,
fired,
firing,
firingStart,
firingLength,
firingIndex,
list = [],
stack = !options.once && [], //如果有once,那么stack就是false,如果没有,那么stack就死[],if条件里面空数组为真,因此在fire()->fireWith()时,就可以再次执行list数组中的方法了。
fire = function( data ) { //这里才是真正执行list数组中方法的地方
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true; //正在触发list数组中的方法
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
//如果回调方法返回false,并且有传入stopOnFalse参数,就会停止for循环,停止执行list数组中后面的方法
memory = false;
break;
}
}
firing = false; //方法执行结束
if ( list ) { //当list数组中的方法执行结束后,就会判断stack是否有值
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() ); //如果有值,就会再次触发fire执行
}
} else if ( memory ) { //如果有once,stack就是false,就会走else
list = []; //如果有传入了memory,只会清空list数组。举个例子:var cb = $.Callbacks("once memory");cb.add(a),cb.fire();cb.add(b);cb.fire()。因为有once,所以只会执行一次,因此第二个fire没用。又由于有memory,所以a,b方法都会执行一次。如果这里没有memory,就会执行下面的else语句,这时只会执行a方法,b方法也不会执行。
} else {
self.disable(); //阻止后面的所有fire操作
}
}
},
self = {
add: function() { //此add方法,就是往list数组中push方法(fire时调用的方法)
if ( list ) { //[]为真
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) { //如果是方法就push到list中
if ( !options.unique || !self.has( arg ) ) { //如果有唯一判断,比如传入unique,就必须判断list数组中是否有此方法了,如果没有,才push到list中。self.has(arg)方法就是判断list数组中是否有arg方法。
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {
add( arg ); //此时add([a,b])是立即执行方法add而不是self.add。
}
});
})( arguments ); //这里处理add(a,b),add([a,b]),同时添加多个方法的情况
if ( firing ) {
firingLength = list.length;
} else if ( memory ) { //第一次调用add时,memory是undefined。当调用fire时,如果你传入了memory,则memory就会变成true,这时你再调用add,就会进入if语句,进行fire。所以你传入memory,fire后,再调用add(b),b方法会执行。
firingStart = start;
fire( memory );
}
}
return this;
},
remove: function() { //去掉list数组中相应的方法
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 ); //主要通过数组的splice方法去掉
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
has: function( fn ) { //jQuery.inArray( fn, list ),如果fn在list中就返回1,不在,就返回-1
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); //如果不传入值,就判断list中是否有值,有值就返回true。
},
empty: function() {
list = [];
firingLength = 0;
return this;
},
disable: function() { //后面所有的操作都失效
list = stack = memory = undefined;
return this;
},
disabled: function() {
return !list;
},
lock: function() { //只会锁住后面的fire操作,让cb.fire方法失效。
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
locked: function() {
return !stack;
},
fireWith: function( context, args ) { //这里执行list数组中的方法
if ( list && ( !fired || stack ) ) { //第一次调用时fired是undefined,第二次调用时,fired为true,就要看stack了。
args = args || []; //如果fire中传了值,就是args,如果没传,就是空数组
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) { //当for循环在执行list数组中的方法时,firing为真,这时调用fire()->fireWith的话,只会把args添加到stack中。当list数组中的方法执行完之后。就会根据stack中是否有值来进行处理。举个例子:var cb = $.Callbacks();function a(){ cb.fire()};funciton b(){};cb.add(a,b);cb.fire();执行a方法,这时又触发fire(),但是不会再次执行a方法,因为这时firing为true,所以只会把stack加1,而是先等b方法执行结束后,才会又重新执行a方法。私有fire方法中会判断stack的值,如果有值,就会继续循环list数组中的方法进行调用执行。
stack.push( args );
} else {
fire( args ); //真正执行list数组中的方法是私有方法fire。
}
}
return this;
},
fire: function() { //fire方法其实就是执行list中的方法
self.fireWith( this, arguments ); //它调用的是fireWith方法.arguments就是fire中传入的参数,它可以给list数组中的方法传值进去
return this;
},
fired: function() {
return !!fired; //只要调用过一次fire就会返回true
}
};
return self; //返回的是self对象。当调用var cb = $.Callbacks(),cb =self。所以调用cb.add,fire,其实就是self.add和fire方法
};
加油!
jquery源码解析:jQuery工具方法Callbacks详解的更多相关文章
- jQuery 源码分析(十八) ready事件详解
ready事件是当DOM文档树加载完成后执行一个函数(不包含图片,css等),因此它的触发要早于load事件.用法: $(document).ready(fun) ;fun是一个函数,这样当DOM树加 ...
- jQuery 源码分析(十一) 队列模块 Queue详解
队列是常用的数据结构之一,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).特点是先进先出,最先插入的元素最先被删除. 在jQuery内部,队列模块为动画模块提供基 ...
- gulp源码解析(一)—— Stream详解
作为前端,我们常常会和 Stream 有着频繁的接触.比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 stream(流)的形式,再通过 .pipe() ...
- jquery源码解析:jQuery延迟对象Deferred(工具方法)详解1
请先看上一课的回调对象.Deferred是通过extend添加到jQuery中的工具方法.如下所示: jQuery.extend({ Deferred: function( func ) { }, w ...
- jquery源码解析:jQuery延迟对象Deferred(工具方法)详解2
请接着上一课继续看. $.Deferred()方法中,有两个对象,一个是deferred对象,一个是promise对象. promise对象有以下几个方法:state,always,then,prom ...
- jQuery源码分析_工具方法(学习笔记)
expando:生成唯一JQ字符串(内部使用) noConflict():防止冲突 isReady:DOM是否加载完成(内部) readyWait:等待多少文件的计数器(内部) holdReady() ...
- jQuery 源码解析(三) pushStack方法 详解
该函数用于创建一个新的jQuery对象,然后将一个DOM元素集合加入到jQuery栈中,最后返回该jQuery对象,有三个参数,如下: elems Array类型 将要压入 jQuery 栈的数组元素 ...
- Spring源码解析--IOC根容器Beanfactory详解
BeanFactory和FactoryBean的联系和区别 BeanFactory是整个Spring容器的根容器,里面描述了在所有的子类或子接口当中对容器的处理原则和职责,包括生命周期的一些约定. F ...
- Linux源码解析-内核栈与thread_info结构详解
1.什么是进程的内核栈? 在内核态(比如应用进程执行系统调用)时,进程运行需要自己的堆栈信息(不是原用户空间中的栈),而是使用内核空间中的栈,这个栈就是进程的内核栈 2.进程的内核栈在计算机中是如何描 ...
随机推荐
- python 文件的读取&更新
[python 文件的读取&更新] 任务抽象: 读取一个文件, 更新内容后, 重新写入文件. 实际应用: 磁盘上的一个配置文件, 读入内存后为一个dict, 对dict更新后重新写入磁盘. d ...
- R包安装失败failed to download mirrors file
在R console中使用install.packages()来安装第三方包时,会出现这样的错误: 即使我们选择的是China的镜像也解决不了问题. 这时候,可以先试试用IE打开上图中黑底部分的URL ...
- 01 lucene基础 北风网项目培训 Lucene实践课程 系统架构
Lucene在搜索的时候数据源可以是文件系统,数据库,web等等. Lucene的搜索是基于索引,Lucene是基于前面建立的索引之上进行搜索的. 使用Lucene就像使用普通的数据库一样. Luce ...
- Avro总结(RPC/序列化)
Avro(读音类似于[ævrə])是Hadoop的一个子项目,由Hadoop的创始人Doug Cutting(也是Lucene,Nutch等项目的创始人,膜拜)牵头开发,当前最新版本1.3.3.Avr ...
- Hibernate批量抓取
------------------siwuxie095 Hibernate 批量抓取 以客户和联系人为例(一对多) 1.批量抓取 同时查询多个对象的关联对象,是 Hibernate 抓取策略的一种 ...
- OSG QT
https://blog.csdn.net/a_Treasure/article/details/82152245 https://www.bbsmax.com/A/kPzOQ4oo5x/ https ...
- Python之多进程和多线程
目标: 1.os.fork简单示例 2.使用os.fork多进程测试IP是否在线 3.使用os.fork多进程解决tcpserver多客户端连接问题 4.多线程测试IP地址是否在线 1.os.fork ...
- 二、SQL的生命周期
MySQL的sql语句由客户端发出,经过连接和权限验证后,最终达到服务器端,由服务器分配thread线程处理,之后就是要介绍的具体服务器端的thread线程是怎么处理每条sql语句的.[ 了解thre ...
- SqlServer垂直分表 如何减少程序改动
当单表数据太多时,我们可以水平划分,参考 SqlServer 分区视图实现水平分表 ,水平划分可以提高表的一些性能. 而 垂直分表 则相对很少见到和用到,因为这可能是数据库设计上的问题了.如果数据库中 ...
- Ajax定时局部刷新
1.局部刷新一个地方 function refreshOnTime(){ $.ajax({ //配置 }); //7秒后重复执行该函数 setInterval('refreshOnTime', 700 ...