前言:

作为参数传递给另一个函数执行的函数我们称为回调函数,那么该回调又是否是异步的呢,何谓异步,如:作为事件处理器,或作为参数传递给

(setTimeout,setInterval)这样的异步函数,或作为ajax发送请求,应用于请求各种状态的处理,我们可以称为异步回调,jQuery.Callbacks

为我们封装了一个回调对象模块,我们先来看一个应用场景:

  1. // 为什么jQuery中的ready事件可以执行多个回调,这得益于我们的jQuery.Deferred递延对象(是基于jQuery.Callbacks回调模块)
  2. jQuery(function($) {
  3. console.log('document is ready!');
  4. // do something
  5. });
  6.  
  7. jQuery(function($) {
  8. // do something
  9. });
  10.  
  11. // 实现原型
  12. // jQuery.Deferred版代码
  13. var df = jQuery.Deferred();
  14. df.resolve(); // 在ready事件中调用
  15.  
  16. // 可以多次执行绑定的函数
  17. df.done(fn1, fn2, fn3);
  18. df.done(fn4);
  19. // ...
  20.  
  21. // jQuery.Callbacks版代码
  22. var cb = jQuery.Callbacks('once memory');
  23. cb.fire(); // 在ready事件中调用
  24.  
  25. // 可以多次执行绑定的函数
  26. cb.add(fn1, fn2, fn3);
  27. cb.add(fn4);
  28. // ...

现在我们知道jQuery中的ready事件是可以这样执行多个回调的,要想深入理解其源码,让我们继续看下面吧

jQuery回调、递延对象总结篇索引:

jQuery回调、递延对象总结(上篇)—— jQuery.Callbacks

jQuery回调、递延对象总结(中篇) —— 神奇的then方法

jQuery回调、递延对象总结(下篇) —— 解密jQuery.when方法

一、jQuery.Callbacks设计思路

使用一个私有变量list(数组)存储回调,执行jQuery.Callbacks函数将返回一个可以操作回调列表list的接口对象,
而传入jQuery.Callbacks函数的options参数则用来控制返回的回调对象操作回调列表的行为

回调对象中的方法

  1. {
  2. add: 增加回调到list
  3. remove: list中移除回调
  4. fire: 触发list中的回调
  5. fired: 回调对象是否执行过fire方法
  6. fireWith: 触发list中的回调,第一个参数为执行域
  7. has: 判断函数是否在list
  8. empty: list致空,list = [];
  9. lock: 锁定list
  10. locked: 是否锁定
  11. disable: 禁用回调对象
  12. disabled: 是否禁用
  13. }

参数标志:

  1. options = {
  2. once: 回调对象仅触发(fire)一次
  3.  
  4. memory: 跟踪记录每一次传递给fire函数的参数,在回调对象触发后(fired),
  5. 将最后一次触发(fire)时的参数(value)传递给在add操作后即将被调用的回调
  6.  
  7. unique: add操作中,相同的函数仅只一次被添加(push)到回调列表中
  8.  
  9. stopOnFalse:当回调函数返回false,中断列表中的回调循环调用,且memory === false,阻止在add操作中将要触发的回调
  10. }

二、源码解析

  1. var // Used for splitting on whitespace
  2. core_rnotwhite = /\S+/g;
  3.  
  4. var optionsCache = {};
  5.  
  6. // Convert String-formatted options into Object-formatted ones and store in cache
  7. // 将字符串格式选项转化成对象格式形式,并存储在缓存对象optionsCache[options]中
  8. // 该缓存起作用适用于执行多次jQuery.Callbacks函数,且传递options参数一致,我们在jQuery.Deferred
  9. // 源码就可以看到tuples二维数组中执行了两次jQuery.Callbacks('once memory')
  10. function createOptions( options ) {
  11. var object = optionsCache[ options ] = {};
  12. jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
  13. object[ flag ] = true;
  14. });
  15. return object;
  16. }
  17. jQuery.Callbacks = function( options ) {
  18.  
  19. // Convert options from String-formatted to Object-formatted if needed
  20. // (we check in cache first)
  21. options = typeof options === "string" ?
  22. ( optionsCache[ options ] || createOptions( options ) ) :
  23. jQuery.extend( {}, options );
  24.  
  25. var // Flag to know if list is currently firing
  26. firing,
  27. // Last fire value (for non-forgettable lists)
  28. memory,
  29. // Flag to know if list was already fired
  30. fired,
  31. // End of the loop when firing
  32. firingLength,
  33. // Index of currently firing callback (modified by remove if needed)
  34. firingIndex,
  35. // First callback to fire (used internally by add and fireWith)
  36. firingStart,
  37. // Actual callback list
  38. list = [],
  39. // Stack of fire calls for repeatable lists
  40. stack = !options.once && [],
  41. // Fire callbacks
  42. fire = function( data ) {
  43. memory = options.memory && data;
  44. fired = true;
  45. firingIndex = firingStart || 0;
  46. firingStart = 0;
  47. firingLength = list.length;
  48. firing = true;
  49. // 迭代list回调列表,列表中的回调被应用(或执行回调)
  50. for ( ; list && firingIndex < firingLength; firingIndex++ ) {
  51. // 如果回调返回false,且options.stopOnFlase === true,则中断循环
  52. // 注:data[1]是一个伪数组(self.fire方法中的arguments(参数集合))
  53. if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
  54. memory = false; // To prevent further calls using add // 阻止在add操作中可能执行的回调
  55. break;
  56. }
  57. }
  58. firing = false;
  59. if ( list ) {
  60. // (options.once === undefined),回调对象可以触发多次
  61. if ( stack ) {
  62. // 处理正在执行的回调中的fireWith操作
  63. // 注:如果执行的回调中真的拥有fire或fireWith操作,那么列表中的回调将会无限循环的执行,请看实例1
  64. if ( stack.length ) {
  65. fire( stack.shift() );
  66. }
  67. }
  68. // (options.once === true && options.memory === true)
  69. // 回调列表致空,但允许add继续添加并执行回调
  70. else if ( memory ) {
  71. list = [];
  72. }
  73. // (options.once === true && options.memory === undefined)
  74. // 禁用回调对象
  75. else {
  76. self.disable();
  77. }
  78. }
  79. },
  80. // Actual Callbacks object
  81. self = {
  82. // Add a callback or a collection of callbacks to the list
  83. add: function() {
  84. if ( list ) {
  85. // First, we save the current length
  86. var start = list.length;
  87. (function add( args ) {
  88. jQuery.each( args, function( _, arg ) {
  89. var type = jQuery.type( arg );
  90. if ( type === "function" ) {
  91. // 回调不唯一 或 唯一且不存在,则push
  92. if ( !options.unique || !self.has( arg ) ) {
  93. list.push( arg );
  94. }
  95. } else if ( arg && arg.length && type !== "string" ) {
  96. // Inspect recursively
  97. // 递归检查
  98. add( arg );
  99. }
  100. });
  101. })( arguments );
  102.  
  103. // Do we need to add the callbacks to the
  104. // current firing batch?
  105. // 如果正在执行的回调执行了add操作,更新firingLength,将列表中新加进来的最后一个回调加入到回调执行的队列中
  106. if ( firing ) {
  107. firingLength = list.length;
  108.  
  109. // With memory, if we're not firing then
  110. // we should call right away
  111. // 如果可能(options.memory === true),在回调对象不能再次fire(options.once === true)时,
  112. // 我们应该使用memory(记录的最后一次fire时的参数)立即调用回调
  113. } else if ( memory ) {
  114. firingStart = start;
  115. fire( memory );
  116. }
  117. }
  118. return this;
  119. },
  120. // Remove a callback from the list
  121. remove: function() {
  122. if ( list ) {
  123. jQuery.each( arguments, function( _, arg ) {
  124. var index;
  125. while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
  126. list.splice( index, 1 );
  127. // Handle firing indexes
  128. // 在回调对象触发(fire)时,如果firingLength、firingIndex(正在执行的回调在列表list中的索引index)
  129. // 大于等于 移除的回调的索引(index),分别减一,确保回调执行队列中未执行的回调依次执行
  130. if ( firing ) {
  131. if ( index <= firingLength ) {
  132. firingLength--;
  133. }
  134. if ( index <= firingIndex ) {
  135. firingIndex--;
  136. }
  137. }
  138. }
  139. });
  140. }
  141. return this;
  142. },
  143. // Check if a given callback is in the list.
  144. // If no argument is given, return whether or not list has callbacks attached.
  145. // 检查给定的回调是否在列表中
  146. // 如果未给定回调参数,返回列表是否有回调
  147. has: function( fn ) {
  148. return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
  149. },
  150. // Remove all callbacks from the list
  151. // 将列表致空,list = []; firingLenght = 0;
  152. empty: function() {
  153. list = [];
  154. firingLength = 0;
  155. return this;
  156. },
  157. // Have the list do nothing anymore
  158. // 禁用回调对象
  159. // 将list赋值为undefined就可以使self中的add,remove,fire,fireWith方法停止工作
  160. // 我认为这里把stack、memory赋值为undefined与否是没有任何关系的
  161. disable: function() {
  162. list = stack = memory = undefined;
  163. return this;
  164. },
  165. // Is it disabled?
  166. disabled: function() {
  167. return !list;
  168. },
  169. // Lock the list in its current state
  170. // 锁定回调列表
  171. // 如果(fired !== true || options.memory === false),则视为禁用(disable)
  172. // 如果(fired === true && options.memory === true),则视为options.once === true
  173. // 请看实例2
  174. lock: function() {
  175. stack = undefined;
  176. if ( !memory ) {
  177. self.disable();
  178. }
  179. return this;
  180. },
  181. // Is it locked?
  182. locked: function() {
  183. return !stack;
  184. },
  185. // Call all callbacks with the given context and arguments
  186. fireWith: function( context, args ) {
  187. // 回调对象未执行过fire 或且 可以执行多次(options.once === false)
  188. // 如果(fired === true && options.once === true),则不会执行fire操作
  189. if ( list && ( !fired || stack ) ) {
  190. args = args || [];
  191. args = [ context, args.slice ? args.slice() : args ];
  192. if ( firing ) {
  193. stack.push( args );
  194. } else {
  195. fire( args );
  196. }
  197. }
  198. return this;
  199. },
  200. // Call all the callbacks with the given arguments
  201. fire: function() {
  202. self.fireWith( this, arguments );
  203. return this;
  204. },
  205. // To know if the callbacks have already been called at least once
  206. fired: function() {
  207. return !!fired;
  208. }
  209. };
  210.  
  211. return self;
  212. };

三、实例

实例1、 处理回调函数中的fire,或fireWidth操作

  1. var cb = jQuery.Callbacks();
  2.  
  3. var fn1 = function(arg){ console.log( arg + '1' ); };
  4. var fn2 = function(arg){ console.log( arg + '2' ); cb.fire(); };
  5. var fn3 = function(arg){ console.log( arg + '3' ); };
  6.  
  7. cb.add(fn1, fn2, fn3);
  8.  
  9. cb.fire('fn'); // 其中回调fn1,fn2,fn3无限制的循环调用
  10.  
  11. /*
  12. 控制台将无限制输出如下:
  13. fn1
  14. fn2
  15. fn3
  16. fn1
  17. fn2
  18. fn3
  19. fn1
  20. fn2
  21. fn3
  22. .
  23. .
  24. .
  25. */

实例2、 锁定(lock)操作各种场景中的用法

  1. var cb1 = jQuery.Callbacks();
  2. var cb2 = jQuery.Callbacks('memory');
  3. var cb3 = jQuery.Callbacks('memory');
  4.  
  5. var fn1 = function(arg){ console.log( arg + '1' ); };
  6. var fn2 = function(arg){ console.log( arg + '2' ); };
  7. var fn3 = function(arg){ console.log( arg + '3' ); };
  8.  
  9. // 如果options.memory !== true,锁定操作视为禁用回调对象
  10. cb1.add(fn1);
  11. cb1.lock();
  12. // 以下操作无任何反应
  13. cb1.add(fn2);
  14. cb1.fire('fn');
  15.  
  16. // 如果fried !== true,锁定操作也视为禁用回调对象
  17. cb2.add(fn1);
  18. cb2.lock();
  19. // 以下操作无任何反应
  20. cb2.add(fn2);
  21. cb2.fire('fn');
  22.  
  23. // 如果(fired === true && options.memory === true),锁定操作类似控制标志once(options.once === true);
  24. cb3.add(fn1);
  25. cb3.fire('fn'); // fn1,此时fired === true
  26. cb3.lock(); // 像是传入了'once'标志,jQuery.Callbacks('once memory');
  27. cb3.add(fn2); // fn2
  28. cb3.fire('fn'); // 再次触发,无任何反应
  29. cb3.add(fn3); // fn3
  30.  
  31. // 再来看看jQuery.Deferred中的一段源码
  32. var tuples = [
  33. // action, add listener, listener list, final state
  34. [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
  35. [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
  36. [ "notify", "progress", jQuery.Callbacks("memory") ]
  37. ];
  38.  
  39. // Handle state
  40. if ( stateString ) {
  41. list.add(function() {
  42. // state = [ resolved | rejected ]
  43. state = stateString;
  44.  
  45. // [ reject_list | resolve_list ].disable; progress_list.lock
  46. }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
  47. }
  48.  
  49. /*
  50. 当执行了tuples中前面两组中任意一个回调对象的fire方法时,后一组回调对象被锁定,
  51. 相当于(fired === true && options.memory === true),后一组回调对象实际为执行
  52. jQuery.Callbacks('once memory')生成的回调对象。
  53. */

PS: 如有描述错误,请帮忙指正,如果你们有不明白的地方也可以发邮件给我,

  如需转载,请附上本文地址及出处:博客园华子yjh,谢谢!

jQuery回调、递延对象总结(上篇)—— jQuery.Callbacks的更多相关文章

  1. jQuery的deferred对象详解 jquery回调函数

    http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html jQuery的 ...

  2. jQuery回调、递延对象总结(一)jQuery.Callbacks详解

    前言: 作为参数传递给另一个函数执行的函数我们称为回调函数,那么该回调又是否是异步的呢,何谓异步,如:作为事件处理器,或作为参数传递给 (setTimeout,setInterval)这样的异步函数, ...

  3. jQuery - 01. jQuery特点、如何使用jQuery、jQuery入口函数、jQuery和DOM对象的区别、jQuery选择器、

    this指的是原生js的DOM对象 .css(“”):只写一个值是取值,写俩值是赋值 window.onload   ===   $(document).ready(); $(“”):获取元素   标 ...

  4. jQuery回调、递延对象总结(下篇) —— 解密jQuery.when方法

    前言: 前一篇文章中重点总结了一下then方法,它主要用来处理多个异步任务按顺序执行,即前一个任务处理完了,再继续下一个,以此类推: 而这一章节jQuery.when方法也是处理多个异步任务,它把多个 ...

  5. jQuery回调、递延对象总结(中篇) —— 神奇的then方法

    前言: 什么叫做递延对象,生成一个递延对象只需调用jQuery.Deferred函数,deferred这个单词译为延期,推迟,即延迟的意思,那么在jQuery中 又是如何表达延迟的呢,从递延对象中的t ...

  6. jQuery回调、递延对象总结

    jQuery回调.递延对象总结(上篇)—— jQuery.Callbacks jQuery回调.递延对象总结(中篇) —— 神奇的then方法 jQuery回调.递延对象总结(下篇) —— 解密jQu ...

  7. jQuery-1.9.1源码分析系列(六) 延时对象续——辅助函数jQuery.when

    $.when的说明 描述: 提供一种方法来执行一个或多个对象的回调函数,返回这些对象的延时(Deferred)对象. 说明(结合实例和源码): 如果你不传递任何参数,  jQuery.when()将返 ...

  8. jQuery的deferred对象学习

    #copy { background-color: lightgreen; padding: 15px; margin: 10px } 一.deferred对象简介 deferred对象是jquery ...

  9. js/jquery 回调函数的定义方法

    基本写法: 带参数的回调函数 以上回调函数,直接传入function作为参数,同样,还可以传入json对象作为参数...如下. 该方法的优势是可以定义多个回调函数....类似$.ajax回调函数中的s ...

随机推荐

  1. DataTable是否存在某个列的判断

    使用 DataTable.Columns.Contains方法可以判断某个列名是否存在于某个DataTable中 //添加模拟数据 DataTable t = new DataTable(); Dat ...

  2. [Xcode 自带svn的使用]

    xcode自带svn的使用 1.代码中 某文件后面有 “M” 标记,表示该文件已被修改,需要commit.            (右键该文件 -> source control -> c ...

  3. Beta版本冲刺第一天 12.5

    一.站立式会议照片: 二.项目燃尽图: 三.项目进展: 成 员 前段时间完成任务 今天完成任务 明天要做任务 问题困难 心得体会 胡泽善 对Alpha版本的分析总结,学习新技术,对Beta版本的讨论规 ...

  4. nginx跨域设置

    nginx跨域问题例子:访问http://10.0.0.10/ 需要能实现跨域 操作:http://10.0.0.10/项目是部署在tomcat里面,tomcat跨域暂时还不会,按照网上的方法操作也没 ...

  5. C#开发和调用Web Service

    http://blog.csdn.net/h0322/article/details/4776819 1.1.Web Service基本概念 Web Service也叫XML Web Service ...

  6. 初学java注解编程 记录错误及解决办法

    1 :在form表单提交到controller层时 利用hbim的封装的访问数据库 form表单中属性要加上method方法 不然不成功. 2 :在运行eclipse时 有时粘贴个数据或者删除个字段老 ...

  7. mysql查看表使用的数据库引擎

    看某个使用的引擎,在显示结果里参数engine后面的就表示该表当前用的存储引擎: mysql> show create table 表名; 看mysql支持哪些存储引擎: mysql> s ...

  8. jquery的$.extend()、$.fn和$.fn.extend()

    一种是类级别的插件开发,即给jquery添加新的全局函数,相当于给jquery类本身添加方法.如$.ajax()等,这就是用$.extend()实现 jquery的全局函数就是属于jquery命名空间 ...

  9. Integer 与int 赋值比较

    测试代码: @Test public void IntegerTest() { Integer i01 = 59; int i02 = 59; Integer i03 = Integer.valueO ...

  10. JavaScript格式化日期

    查找格式化日期的方法大都是写日期扩展方法,也许是为了维持jquery easyUI 源码完整性, // 对Date的扩展,将 Date 转化为指定格式的String // 月(M).日(d).小时(h ...