jQuery回调、递延对象总结(上篇)—— jQuery.Callbacks
前言:
作为参数传递给另一个函数执行的函数我们称为回调函数,那么该回调又是否是异步的呢,何谓异步,如:作为事件处理器,或作为参数传递给
(setTimeout,setInterval)这样的异步函数,或作为ajax发送请求,应用于请求各种状态的处理,我们可以称为异步回调,jQuery.Callbacks
为我们封装了一个回调对象模块,我们先来看一个应用场景:
- // 为什么jQuery中的ready事件可以执行多个回调,这得益于我们的jQuery.Deferred递延对象(是基于jQuery.Callbacks回调模块)
- jQuery(function($) {
- console.log('document is ready!');
- // do something
- });
- jQuery(function($) {
- // do something
- });
- // 实现原型
- // jQuery.Deferred版代码
- var df = jQuery.Deferred();
- df.resolve(); // 在ready事件中调用
- // 可以多次执行绑定的函数
- df.done(fn1, fn2, fn3);
- df.done(fn4);
- // ...
- // jQuery.Callbacks版代码
- var cb = jQuery.Callbacks('once memory');
- cb.fire(); // 在ready事件中调用
- // 可以多次执行绑定的函数
- cb.add(fn1, fn2, fn3);
- cb.add(fn4);
- // ...
现在我们知道jQuery中的ready事件是可以这样执行多个回调的,要想深入理解其源码,让我们继续看下面吧
jQuery回调、递延对象总结篇索引:
jQuery回调、递延对象总结(上篇)—— jQuery.Callbacks
jQuery回调、递延对象总结(中篇) —— 神奇的then方法
jQuery回调、递延对象总结(下篇) —— 解密jQuery.when方法
一、jQuery.Callbacks设计思路
使用一个私有变量list(数组)存储回调,执行jQuery.Callbacks函数将返回一个可以操作回调列表list的接口对象,
而传入jQuery.Callbacks函数的options参数则用来控制返回的回调对象操作回调列表的行为
回调对象中的方法
- {
- add: 增加回调到list中
- remove: 从list中移除回调
- fire: 触发list中的回调
- fired: 回调对象是否执行过fire方法
- fireWith: 触发list中的回调,第一个参数为执行域
- has: 判断函数是否在list中
- empty: 将list致空,list = [];
- lock: 锁定list
- locked: 是否锁定
- disable: 禁用回调对象
- disabled: 是否禁用
- }
参数标志:
- options = {
- once: 回调对象仅触发(fire)一次
- memory: 跟踪记录每一次传递给fire函数的参数,在回调对象触发后(fired),
- 将最后一次触发(fire)时的参数(value)传递给在add操作后即将被调用的回调
- unique: 在add操作中,相同的函数仅只一次被添加(push)到回调列表中
- stopOnFalse:当回调函数返回false,中断列表中的回调循环调用,且memory === false,阻止在add操作中将要触发的回调
- }
二、源码解析
- var // Used for splitting on whitespace
- core_rnotwhite = /\S+/g;
- var optionsCache = {};
- // Convert String-formatted options into Object-formatted ones and store in cache
- // 将字符串格式选项转化成对象格式形式,并存储在缓存对象optionsCache[options]中
- // 该缓存起作用适用于执行多次jQuery.Callbacks函数,且传递options参数一致,我们在jQuery.Deferred
- // 源码就可以看到tuples二维数组中执行了两次jQuery.Callbacks('once memory')
- function createOptions( options ) {
- var object = optionsCache[ options ] = {};
- jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
- object[ flag ] = true;
- });
- return object;
- }
- 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 && [],
- // Fire callbacks
- fire = function( data ) {
- memory = options.memory && data;
- fired = true;
- firingIndex = firingStart || 0;
- firingStart = 0;
- firingLength = list.length;
- firing = true;
- // 迭代list回调列表,列表中的回调被应用(或执行回调)
- for ( ; list && firingIndex < firingLength; firingIndex++ ) {
- // 如果回调返回false,且options.stopOnFlase === true,则中断循环
- // 注:data[1]是一个伪数组(self.fire方法中的arguments(参数集合))
- if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
- memory = false; // To prevent further calls using add // 阻止在add操作中可能执行的回调
- break;
- }
- }
- firing = false;
- if ( list ) {
- // (options.once === undefined),回调对象可以触发多次
- if ( stack ) {
- // 处理正在执行的回调中的fireWith操作
- // 注:如果执行的回调中真的拥有fire或fireWith操作,那么列表中的回调将会无限循环的执行,请看实例1
- if ( stack.length ) {
- fire( stack.shift() );
- }
- }
- // (options.once === true && options.memory === true)
- // 回调列表致空,但允许add继续添加并执行回调
- else if ( memory ) {
- list = [];
- }
- // (options.once === true && options.memory === undefined)
- // 禁用回调对象
- 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 ) {
- var type = jQuery.type( arg );
- if ( type === "function" ) {
- // 回调不唯一 或 唯一且不存在,则push
- 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?
- // 如果正在执行的回调执行了add操作,更新firingLength,将列表中新加进来的最后一个回调加入到回调执行的队列中
- if ( firing ) {
- firingLength = list.length;
- // With memory, if we're not firing then
- // we should call right away
- // 如果可能(options.memory === true),在回调对象不能再次fire(options.once === true)时,
- // 我们应该使用memory(记录的最后一次fire时的参数)立即调用回调
- } 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
- // 在回调对象触发(fire)时,如果firingLength、firingIndex(正在执行的回调在列表list中的索引index)
- // 大于等于 移除的回调的索引(index),分别减一,确保回调执行队列中未执行的回调依次执行
- 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
- // 将列表致空,list = []; firingLenght = 0;
- empty: function() {
- list = [];
- firingLength = 0;
- return this;
- },
- // Have the list do nothing anymore
- // 禁用回调对象
- // 将list赋值为undefined就可以使self中的add,remove,fire,fireWith方法停止工作
- // 我认为这里把stack、memory赋值为undefined与否是没有任何关系的
- disable: function() {
- list = stack = memory = undefined;
- return this;
- },
- // Is it disabled?
- disabled: function() {
- return !list;
- },
- // Lock the list in its current state
- // 锁定回调列表
- // 如果(fired !== true || options.memory === false),则视为禁用(disable)
- // 如果(fired === true && options.memory === true),则视为options.once === true
- // 请看实例2
- 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 ) {
- // 回调对象未执行过fire 或且 可以执行多次(options.once === false)
- // 如果(fired === true && options.once === true),则不会执行fire操作
- 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;
- };
三、实例
实例1、 处理回调函数中的fire,或fireWidth操作
- var cb = jQuery.Callbacks();
- var fn1 = function(arg){ console.log( arg + '1' ); };
- var fn2 = function(arg){ console.log( arg + '2' ); cb.fire(); };
- var fn3 = function(arg){ console.log( arg + '3' ); };
- cb.add(fn1, fn2, fn3);
- cb.fire('fn'); // 其中回调fn1,fn2,fn3无限制的循环调用
- /*
- 控制台将无限制输出如下:
- fn1
- fn2
- fn3
- fn1
- fn2
- fn3
- fn1
- fn2
- fn3
- .
- .
- .
- */
实例2、 锁定(lock)操作各种场景中的用法
- var cb1 = jQuery.Callbacks();
- var cb2 = jQuery.Callbacks('memory');
- var cb3 = jQuery.Callbacks('memory');
- var fn1 = function(arg){ console.log( arg + '1' ); };
- var fn2 = function(arg){ console.log( arg + '2' ); };
- var fn3 = function(arg){ console.log( arg + '3' ); };
- // 如果options.memory !== true,锁定操作视为禁用回调对象
- cb1.add(fn1);
- cb1.lock();
- // 以下操作无任何反应
- cb1.add(fn2);
- cb1.fire('fn');
- // 如果fried !== true,锁定操作也视为禁用回调对象
- cb2.add(fn1);
- cb2.lock();
- // 以下操作无任何反应
- cb2.add(fn2);
- cb2.fire('fn');
- // 如果(fired === true && options.memory === true),锁定操作类似控制标志once(options.once === true);
- cb3.add(fn1);
- cb3.fire('fn'); // fn1,此时fired === true
- cb3.lock(); // 像是传入了'once'标志,jQuery.Callbacks('once memory');
- cb3.add(fn2); // fn2
- cb3.fire('fn'); // 再次触发,无任何反应
- cb3.add(fn3); // fn3
- // 再来看看jQuery.Deferred中的一段源码
- var tuples = [
- // action, add listener, listener list, final state
- [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
- [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
- [ "notify", "progress", jQuery.Callbacks("memory") ]
- ];
- // Handle state
- if ( stateString ) {
- list.add(function() {
- // state = [ resolved | rejected ]
- state = stateString;
- // [ reject_list | resolve_list ].disable; progress_list.lock
- }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
- }
- /*
- 当执行了tuples中前面两组中任意一个回调对象的fire方法时,后一组回调对象被锁定,
- 相当于(fired === true && options.memory === true),后一组回调对象实际为执行
- jQuery.Callbacks('once memory')生成的回调对象。
- */
PS: 如有描述错误,请帮忙指正,如果你们有不明白的地方也可以发邮件给我,
jQuery回调、递延对象总结(上篇)—— jQuery.Callbacks的更多相关文章
- jQuery的deferred对象详解 jquery回调函数
http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html jQuery的 ...
- jQuery回调、递延对象总结(一)jQuery.Callbacks详解
前言: 作为参数传递给另一个函数执行的函数我们称为回调函数,那么该回调又是否是异步的呢,何谓异步,如:作为事件处理器,或作为参数传递给 (setTimeout,setInterval)这样的异步函数, ...
- jQuery - 01. jQuery特点、如何使用jQuery、jQuery入口函数、jQuery和DOM对象的区别、jQuery选择器、
this指的是原生js的DOM对象 .css(“”):只写一个值是取值,写俩值是赋值 window.onload === $(document).ready(); $(“”):获取元素 标 ...
- jQuery回调、递延对象总结(下篇) —— 解密jQuery.when方法
前言: 前一篇文章中重点总结了一下then方法,它主要用来处理多个异步任务按顺序执行,即前一个任务处理完了,再继续下一个,以此类推: 而这一章节jQuery.when方法也是处理多个异步任务,它把多个 ...
- jQuery回调、递延对象总结(中篇) —— 神奇的then方法
前言: 什么叫做递延对象,生成一个递延对象只需调用jQuery.Deferred函数,deferred这个单词译为延期,推迟,即延迟的意思,那么在jQuery中 又是如何表达延迟的呢,从递延对象中的t ...
- jQuery回调、递延对象总结
jQuery回调.递延对象总结(上篇)—— jQuery.Callbacks jQuery回调.递延对象总结(中篇) —— 神奇的then方法 jQuery回调.递延对象总结(下篇) —— 解密jQu ...
- jQuery-1.9.1源码分析系列(六) 延时对象续——辅助函数jQuery.when
$.when的说明 描述: 提供一种方法来执行一个或多个对象的回调函数,返回这些对象的延时(Deferred)对象. 说明(结合实例和源码): 如果你不传递任何参数, jQuery.when()将返 ...
- jQuery的deferred对象学习
#copy { background-color: lightgreen; padding: 15px; margin: 10px } 一.deferred对象简介 deferred对象是jquery ...
- js/jquery 回调函数的定义方法
基本写法: 带参数的回调函数 以上回调函数,直接传入function作为参数,同样,还可以传入json对象作为参数...如下. 该方法的优势是可以定义多个回调函数....类似$.ajax回调函数中的s ...
随机推荐
- DataTable是否存在某个列的判断
使用 DataTable.Columns.Contains方法可以判断某个列名是否存在于某个DataTable中 //添加模拟数据 DataTable t = new DataTable(); Dat ...
- [Xcode 自带svn的使用]
xcode自带svn的使用 1.代码中 某文件后面有 “M” 标记,表示该文件已被修改,需要commit. (右键该文件 -> source control -> c ...
- Beta版本冲刺第一天 12.5
一.站立式会议照片: 二.项目燃尽图: 三.项目进展: 成 员 前段时间完成任务 今天完成任务 明天要做任务 问题困难 心得体会 胡泽善 对Alpha版本的分析总结,学习新技术,对Beta版本的讨论规 ...
- nginx跨域设置
nginx跨域问题例子:访问http://10.0.0.10/ 需要能实现跨域 操作:http://10.0.0.10/项目是部署在tomcat里面,tomcat跨域暂时还不会,按照网上的方法操作也没 ...
- C#开发和调用Web Service
http://blog.csdn.net/h0322/article/details/4776819 1.1.Web Service基本概念 Web Service也叫XML Web Service ...
- 初学java注解编程 记录错误及解决办法
1 :在form表单提交到controller层时 利用hbim的封装的访问数据库 form表单中属性要加上method方法 不然不成功. 2 :在运行eclipse时 有时粘贴个数据或者删除个字段老 ...
- mysql查看表使用的数据库引擎
看某个使用的引擎,在显示结果里参数engine后面的就表示该表当前用的存储引擎: mysql> show create table 表名; 看mysql支持哪些存储引擎: mysql> s ...
- jquery的$.extend()、$.fn和$.fn.extend()
一种是类级别的插件开发,即给jquery添加新的全局函数,相当于给jquery类本身添加方法.如$.ajax()等,这就是用$.extend()实现 jquery的全局函数就是属于jquery命名空间 ...
- Integer 与int 赋值比较
测试代码: @Test public void IntegerTest() { Integer i01 = 59; int i02 = 59; Integer i03 = Integer.valueO ...
- JavaScript格式化日期
查找格式化日期的方法大都是写日期扩展方法,也许是为了维持jquery easyUI 源码完整性, // 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h ...