Callbacks模块实质上就是一个回调函数队列(当然吹得很牛逼。。比如“提供了一种强大的方法来管理回调函数队列”),之所以介绍它是因为后面的Derferred模块基于它。

Callbacks生成时接收四个设置:once(只触发一遍),memory(记录前一次的触发传入参数,disable时是否清空队列),unique(确保队列中同样的函数只有一个),stopOnFalse(当调用某一个回调函数返回false时则停止触发)

例:jQuery.Callbacks('once memory')

Callbacks模块还有几个API,add,remove,has,empty,disable,lock,fire,fireWith(根据指定的上下文触发),以及两个状态判断函数,locked和disabled。

这几个API的用法就像名字所述的那样,增加啊,移除啊之类的。。更多信息可以看文档

这个模块没什么难度,在编写时主要有两个点需要考虑:1,当我添加或者删除某个函数时,该队列正在触发怎么办?2,当我要触发某一个队列时,该队列正在触发怎么办?

答:记录状态,做处理即可

//因为队列可能在调用时被改变,所以需要考虑两种状态,没调用和调用时。

jQuery.Callbacks = function( options ) {

	// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
//这样处理一下以后调用就可以保证options存在,不会像我option&&option.xxx
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options ); var // Flag to know if list is currently firing
firing,
// Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// First callback to fire (used internally by add and fireWith)
firingStart,
// Actual callback list
list = [],
// Stack of fire calls for repeatable lists
//这里的stack存的不是fn,而是传给fn调用的arguments
//这里名称上是stack,实际上是queue
stack = !options.once && [],
// Fire callbacks
//data[0]是上下文
//data[1]是传给各个回调函数的参数
fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
//当传入的data[0]为数组时,函数调用时的this依然是这个数组,而非数组中的item
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
//这里主要考虑在执行的过程中会再次执行,所以用stack来保存传入的参数
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// First, we save the current length
var start = list.length;
//这里用闭包函数的主要原因是要递归调用,质疑
(function add( args ) {
jQuery.each( args, function( _, arg ) {
console.log('list');
console.log(list);
var type = jQuery.type( arg );
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
//当传入的参数是多维数组时递归调用,有必要这样处理吗?是后面有这种用法
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
console.log('递归');
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
//如果有memory,但我们又不是正在触发,所以要立刻触发后面增加的函数
//这里可以思考一下
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
//删除很简单就删除了
list.splice( index, 1 );
// Handle firing indexes
//需要考虑正在触发的情况
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// Remove all callbacks from the list
empty: function() {
list = [];
firingLength = 0;
return this;
},
// Have the list do nothing anymore
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
disabled: function() {
return !list;
},
// Lock the list in its current state
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
// Is it locked?
locked: function() {
return !stack;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
}; return self;
};

jQuery1.11源码分析(10)-----Callbacks模块的更多相关文章

  1. jQuery1.11源码分析(1)-----Sizzle源码概览[原创]

    最近在啃jQuery1.11源码,上来就遇到Sizzle这个jQuery的大核心,虽然已经清楚了Sizzle的用途,先绕过去也没事,但明知山有虎偏向虎山行才是我们要做的. 本文面向的阅读对象:正在学习 ...

  2. jQuery1.11源码分析(5)-----Sizzle编译和过滤阶段[原创]

    在上一章中,我们说到在之前的查找阶段我们已经获得了待选集seed,那么这一章我们就来讲如何将seed待选集过滤,以获得我们最终要用的元素. 其实思路本质上还是不停地根据token过滤,但compile ...

  3. jQuery1.11源码分析(7)-----jQuery一些基本的API

    这篇文章比较繁杂,主要就是把jQuery源码从上到下列出来,看我的注释就好了. jQuery源码对各种加载器做了处理. //阅读这个源码是请先了解一下概念,即时函数,工厂模式 (function( g ...

  4. jQuery1.11源码分析(6)-----jQuery结构总揽

    (在看以下内容之前请先对原型链有一定的了解,比如:prototype是对象还是函数?) 在看jQuery的其他源码之前,必须对jQuery的数据结构有一定的了解. jQuery的核心很简单,jQuer ...

  5. jQuery1.11源码分析(2)-----Sizzle源码中的正则表达式[原创]

    看完了上篇,对Sizzle有了一个大致的了解,我们接下来就可以正式开始啃Sizzle的源码了.上来就讲matcher难度太大,先来点开胃菜,讲讲Sizzle中的各个正则表达式的作用吧(本来还想讲初始化 ...

  6. jQuery1.11源码分析(3)-----Sizzle源码中的浏览器兼容性检测和处理[原创]

    上一章讲了正则表达式,这一章继续我们的前菜,浏览器兼容性处理. 先介绍一个简单的沙盒测试函数. /** * Support testing using an element * @param {Fun ...

  7. jQuery1.11源码分析(9)-----初始化jQuery对象的函数和关联节点获取函数

    这篇也没什么好说的,初始化jQuery对象的函数要处理多种情况,已经被寒冬吐槽烂了.关联节点获取函数主要基于两个工具函数dir和sibling,前者基于指定的方向遍历,后者则遍历兄弟节点(真的不能合并 ...

  8. jQuery1.11源码分析(8)-----jQuery调用Sizzle引擎的相关API

    之所以把这部分放在这里,是因为这里用到了一些基本API,前一篇介绍过后才能使用. //jQuery通过find方法调用Sizzle引擎 //jQuery通过find方法调用Sizzle引擎 jQuer ...

  9. jQuery1.11源码分析(4)-----Sizzle工厂函数[原创]

    在用前两篇讲述完正则表达式.初始化.特性检测之后,终于到了我们的正餐——Sizzle工厂函数! Sizzle工厂函数有四个参数, selector:选择符 context:查找上下文 results: ...

随机推荐

  1. initializer for conditional binding must have optional type not AVAudioPlayer

    if let buttonBeep = self.setupAudioPlayerWithFile("ButtonTap", type: "wav") {    ...

  2. Jenkins进阶系列之——05FTP publisher plugin插件

    说明:这个插件可以将构建的产物(例如:Jar)发布到FTP中去. 官方说明:FTP publisher plugin 安装步骤: 系统管理→管理插件→可选插件→Artifact Uploaders→F ...

  3. 使用OneNote的COM组件,实现OCR功能。

    背景 在业务系统开发的过程中,很多情况下会去识别图片中的相关信息,并且把信息录入到系统中.现在希望通过自动化的方式录入,就有了以下的工作.在对比了几个OCR软件在中文识别方面的准确率后,决定使用微软的 ...

  4. 使用spring cloud实现分布式配置管理

    <7天学会spring cloud系列>之创建配置管理服务器及实现分布式配置管理应用. 本文涉及到的项目: 开源项目:http://git.oschina.net/zhou666/spri ...

  5. Angular JS 学习笔记(自定义服务:factory,Promise 模式异步请求查询:$http,过滤器用法filter,指令:directive)

    刚学没多久,作了一个小项目APP,微信企业号开发与微信服务号的开发,使用的是AngularJS开发,目前项目1.0版本已经完结,但是项目纯粹为了赶工,并没有发挥AngularJS的最大作用,这几天项目 ...

  6. Java学习笔记(六)——google java编程风格指南(下)

    [前面的话] 年后开始正式上班,计划着想做很多事情,但是总会有这样那样的打扰,不知道是自己要求太高还是自我的奋斗意识不够?接下来好好加油.好好学学技术,好好学习英语,好好学习做点自己喜欢的事情,趁着自 ...

  7. iptables 的使用

    iptables 是Linux 防火墙规则配置命令 iptables -L -n 查看目前配置 iptables -F        清除预设表filter中的所有规则链的规则 iptables -A ...

  8. 每天一个linux命令(41):at命令

    在windows系统中,windows提供了计划任务这一功能,在控制面板 -> 性能与维护 -> 任务计划, 它的功能就是安排自动运行的任务. 通过'添加任务计划'的一步步引导,则可建立一 ...

  9. 每天一个linux命令(30):cal 命令

    cal命令可以用来显示公历(阳历)日历.公历是现在国际通用的历法,又称格列历,通称阳历.“阳历”又名“太阳历”,系以地球绕行太阳一周为一年,为西方各国所通用,故又名“西历”. 1.命令格式: cal  ...

  10. C头文件之<cstring>

    (string.h) 这个文件夹主要是定义了几个对字符串和数组进行操作的函数.功能很强大.下面是重要函数: strcpy.strncpy strcpy,strncpy 这两个函数是对字符串的复制,很常 ...