借用百度百科来说明下回调函数:

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

jQuery回调对象实现恰好利用了设计模式中的观察者模式思想,观察者模式 (pub/sub) 的背后,总的想法是在应用程序中增强松耦合性。并非是在其它对象的方法上的单个对象调用。一个对象作为特定任务或是另一对象的活动的观察者,并且在这个任务或活动发生时,通知观察者。观察者也被叫作订阅者(Subscriber),它指向被观察的对象,既被观察者(Publisher 或 subject)。当事件发生时,被观察者(Publisher)就会通知观察者(subscriber)

具体到开发中,我们会在事件触发,定时器,ajax、动画(transformend)使用回调函数。

jQuery中回调函数队列管理模块:Callbacks从1.6版中的_Deferred对象中抽离出来的。其设计原理是开始构建一个存放回调的数组,再通过add、remove、fire、lock等操作来控制函数队列的管理,并提供once、memory、unique、stopOnFalse四个option进行一些特殊的控制。

代码如下:

var rnotwhite = (/\S+/g);//匹配空格,返回数组

// String to Object options format cache
var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {//接受的字符串的组合传参数,可以使用空格分割
object[ flag ] = true;
});
return object;
} /*
* Create a callback list using the following parameters: * By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options://这里提供四个可选参数once、memory、unique、stopOnFalse 对函数队列进行特殊控制。
*/
jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
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 = !options.once && [],//传递once参数后,则回调执行完毕之后,list会被清空([])或置为undefined
// Fire callbacks
fire = function( data ) {//闭包方法
memory = options.memory && data;
fired = true;//用来判断回调队列是否被执行过一次
firingIndex = firingStart || 0;//上次fire的位置,用作设置memory时,add之后直接触发回调。
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//判断是否需要判读回调返回值
memory = false; // To prevent further calls using add---//强制修改memory
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {//是否为false,初始化时是通过是否传递once来控制,
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {//这里清空目的在于,add之后直接fire新添加的回调。
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 ) {//添加回调list集合
jQuery.each( args, function( _, arg ) {
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
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
} else if ( memory ) {//这里如果option.memory有值的话,memory已经被修改为[context,arguments]
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 );//若$.Callback调用时设置memory,则会闭包中memory被修改为此时的args<==>[context,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;
};

1、功能上看:callbacks可以用来在队列中添加回调,执行回调,删除回调等等。并提供一些参数如once,memory,unique等来进行特殊需求的控制。

2、回调模块结构组织:首先构建一个存放回调的队列,如var list = [],通过闭包使回调队列所占内存空间不被释放添加回调时,将回调push进list,fire时遍历list将回调队列中函数执行一遍。

jQuery.Callbacks()的API列表如下

callbacks.add()        往回调队列中添加一个回调或回调的集合。
callbacks.disable() 禁用回调列表中的回调
callbacks.disabled() 判断回调列表是否已被禁用。
callbacks.empty() 从列表中删除所有的回调.
callbacks.fire() 用给定的参数执行所有的回调
callbacks.fired() 判断是否回调队列是否被至少fire一次。
callbacks.fireWith() 访问给定的上下文和参数列表中的执行所有回调。
callbacks.has() 确定列表中是否包含一个回调
callbacks.lock() 锁定当前状态的回调列表。
callbacks.locked() 确定回调列表是否已被锁定。
callbacks.remove() 从回调列表中的删除一个回调或回调集合。

代码定义了多个局部变量,来看看他们用处:

list:保存回掉函数队列  通过闭包使回调数组所占内存空间不被释放,添加回调时,将回调push进list,执行时则遍历list执行回调

firing:判断是否正在执行回调队列。

firingStart:记住当前fire的位置,当参数为memory时使用到。

stack:如果当前正在执行回调(或者说回掉队列未执行完),即firing为true,将要fire的回调保存到栈中,若未设置了once为true ,待回调队列执行完成之后就会执行该回掉。若设置了once 则确保这个回调列表只执行( .fire() )一次。

memory :若设置了memory,保持以前的值,后面添加到这个回调队列会立即执行该回调(像一个递延 Deferred)

firingStart:用于add 回调之后作为立即执行该回调的索引(在设置memory之后使用)

重点提下fire方法: self.fire –> self.fireWith –> fire 最终执行代码是内部私有的fire方法,内部几个逻辑处理了以下几种情况

(1)、每fire 一次都遍历list回调队列,直到结束或者有一个回调函数返回false (只有设置了stopOnFalse控制逻辑才起作用),才中止执行回调

(2)、接下来处理了,回调队列正在执行时(firing为true),add 了一个回调,并不会立即处理而是push 到stack之后。 待回调队列执行完成后再根据stack判断是否fire新增的回调。

disable方法:list = stack = memory = undefined; 后续所有操作都失效

3、参数的使用方式:参数用 一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为:通过以下这些参数进行特殊需求的控制

  • once: 确保这个回调列表只执行( .fire() )一次.
  • memory: 保持以前的值,当执行add之后,将运行添加到这个列表的后面的最新的回调,参数保存在memory中(像一个递延 Deferred).
  • unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调).
  • stopOnFalse: 当一个回调返回false 时中断调用

当新建一个Callbacks回调队列时,可以通过这些参数定义回调队列fire 之后的行为。

默认情况下,回调列表(不传递参数情况下)可以被多次触发(fire),下面来看看参数具体如何使用及其在源码中如何发挥作用的。

(1)、var callbacks = $.Callbacks( "once" ); —— fire一次之后list被清空

(2)、var callbacks = $.Callbacks( "memory" );—— memory的实现思路就是回调队列list在fire之后(不管是否设置once),以后add的时候自动调用fire,将新添加的回调执行一遍。(类似递延)

(3)、var callbacks = $.Callbacks( "unique" );——控制回调函数的唯一性。通过has方法判断是否存在队列中。

(4)、var callbacks = $.Callbacks( "stopOnFalse" );——控制是否需要回调函数返回值是否为false来终止回调队列的执行。

(5)、var callbacks = $.Callbacks( "once memory" );—— 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调,once的时候只允许add一次,在触发fire之后就会理清掉list。

jquery源码分析(四)——回调对象 Callbacks的更多相关文章

  1. jQuery 源码分析(四) each函数 $.each和$.fn.each方法 详解

    $.each一般用来遍历一个数组或对象,$.fn.each()就是指jQuery实例可以执行的操作(因为$.fn是jQuery对象的原型) $.each用来遍历一个数组或对象,并依次执行回掉函数,最后 ...

  2. Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号

    Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...

  3. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  4. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  5. jQuery源码分析系列(转载来源Aaron.)

    声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...

  6. jQuery源码分析系列——来自Aaron

    jQuery源码分析系列——来自Aaron 转载地址:http://www.cnblogs.com/aaronjs/p/3279314.html 版本截止到2013.8.24 jQuery官方发布最新 ...

  7. jQuery源码分析-each函数

    本文部分截取自且行且思 jQuery.each方法用于遍历一个数组或对象,并对当前遍历的元素进行处理,在jQuery使用的频率非常大,下面就这个函数做了详细讲解: 复制代码代码 /*! * jQuer ...

  8. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  9. jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)

    在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...

随机推荐

  1. Android开发:setAlpha()方法

    paint.setAlpha() 即透明度.其取值范围是0---255,数值越小,越透明,颜色上表现越淡. 实际上当设成10以下就会有透明的效果了.

  2. 主表a主表b 从表c中有ab两个表中各一个字段a1,b1 从表d中有ab两个表中各一个字段a2,b2

    a1和a2在a表中具有唯一性 b1和b2在b表中具有唯一性 现在需要连接c表和d表 需要分两步来做 1.先让c表join表a和表b select c.*,a.a2,b.b2 from c inner ...

  3. 【POJ 3784】 Running Median

    [题目链接] http://poj.org/problem?id=3784 [算法] 对顶堆算法 要求动态维护中位数,我们可以将1-M/2(向下取整)小的数放在大根堆中,M/2+1-M小的数放在小根堆 ...

  4. perl数组的长度与元素个数

    perl数组的长度与元素个数 $#数组名 ---表示数组中最后一个元素的下标,它等于元素个数减1. @数组名 ---表示数组中元素的个数. $标量=@数组名 ---将一个数组赋值给一个标量变量,标量得 ...

  5. bzoj4299

    主席树+脑洞 首先我们有一个结论:如果我们已经凑出1-n,那么下一个数必须小于等于n+1才能凑出n+1,否则结束. 那么如果只有一次询问,我们把数组排序,然后扫一遍看每个数当前能不能加入.但是多组询问 ...

  6. bzoj2648

    http://www.lydsy.com/JudgeOnline/problem.php?id=2648 kdtree裸题... 抄板子一边抄对了... 挺好理解的,就是说我们先找出中间的元素,然后小 ...

  7. js:正则表达

    一:正则表达对象方法 1:compile()方法   //编译正则表达式 实例:在字符串中全局搜索“man”,并用“person”替换,然后通过compile()方法,改变正则表达式,用person替 ...

  8. [Swift通天遁地]四、网络和线程-(5)解析网络请求数据:String(字符串)、Data(二进制数据)和JSON数据

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  9. python自动化测试学习笔记-4内置函数,处理json

    函数.全局变量 写代码时注意的几点事项: 1.一般写代码的时候尽量少用或不用全局变量,首先全局变量不安全,大家协作的情况下,代码公用容易被篡改,其次全局变量会一直占用系统内容. 2.函数里如果有多个r ...

  10. python django简单操作

    准备: pip3 install  django==1.10.3 cmd django-admin startproject  guest  创建一个guest的项目 cd guest manage. ...