jQuery中有一个很实用的函数队列,可能我们很少用到,但他在jQuery内部却有着举足轻重的地位。

他就是Callbacks. jQuery作者用它构建了很多非常重要的模块。比如说$.Deferred。

Callbacks 说白了就是个数组,里面存了很多函数对象。然而他真的 just so so么?

好吧,爱因斯坦也只是个人,但他真的仅仅是个普普通通的人吗?Callbacks也不是。

不说废话了,先看源码。

// 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.split( core_rspace ), function( _, flag ) {object[ flag ] = true;});return object;
}/** Create a callback list using the following parameters:**   options: an optional list of space-separated options that will change how*          the callback list behaves or a more traditional option object** By default a callback list will act like an event callback list and can be* "fired" multiple times.** Possible options:** once:           will ensure the callback list can only be fired once (like a Deferred)**    memory:         will keep track of previous values and will call any callback added*                    after the list has been fired right away with the latest "memorized"*                 values (like a Deferred)**  unique:         will ensure a callback can only be added once (no duplicate in the list)**  stopOnFalse:    interrupt callings when a callback returns false**/
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 // Last fire value (for non-forgettable lists)memory,// Flag to know if list was already firedfired,// Flag to know if list is currently firingfiring,// First callback to fire (used internally by add and fireWith)firingStart,// End of the loop when firingfiringLength,// Index of currently firing callback (modified by remove if needed)firingIndex,// Actual callback listlist = [],// Stack of fire calls for repeatable listsstack = !options.once && [],// Fire callbacksfire = function( data ) {memory = options.memory && data;fired = true;firingIndex = firingStart || 0;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 addbreak;}}firing = false;if ( list ) {if ( stack ) {if ( stack.length ) {fire( stack.shift() );}} else if ( memory ) {list = [];} else {self.disable();}}},// Actual Callbacks objectself = {// Add a callback or a collection of callbacks to the listadd: function() {if ( list ) {// First, we save the current lengthvar start = list.length;(function add( args ) {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 recursivelyadd( 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 ) {firingStart = start;fire( memory );}}return this;},// Remove a callback from the listremove: function() {if ( list ) {jQuery.each( arguments, function( _, arg ) {var index;while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {list.splice( index, 1 );// Handle firing indexesif ( firing ) {if ( index <= firingLength ) {firingLength--;}if ( index <= firingIndex ) {firingIndex--;}}}});}return this;},// Control if a given callback is in the listhas: function( fn ) {return jQuery.inArray( fn, list ) > -1;},// Remove all callbacks from the listempty: function() {list = [];return this;},// Have the list do nothing anymoredisable: function() {list = stack = memory = undefined;return this;},// Is it disabled?disabled: function() {return !list;},// Lock the list in its current statelock: function() {stack = undefined;if ( !memory ) {self.disable();}return this;},// Is it locked?locked: function() {return !stack;},// Call all callbacks with the given context and argumentsfireWith: function( context, args ) {args = args || [];args = [ context, args.slice ? args.slice() : args ];if ( list && ( !fired || stack ) ) {if ( firing ) {stack.push( args );} else {fire( args );}}return this;},// Call all the callbacks with the given argumentsfire: function() {self.fireWith( this, arguments );return this;},// To know if the callbacks have already been called at least oncefired: function() {return !!fired;}};return self;
};

代码只有仅仅200行不到,但真正看起来却又点绕,

《think in java》中有这么一句,理解一个程序最好的方法,就是把它看做一个服务的提供者。

那他提供了那些服务:

首先我们看看返回的self对象

{// 添加方法add: function() {},// 删除remove: function() {},// 是否包含has: function() {},// 清空empty: function() {},// 禁用disable: function() {},// 加锁lock: function() {},// 是否加锁locked: function() {},// 触发fireWith: function(){},fire: function() {},// 是否触发fired: function() {}
}

  用途都十分清晰,那我们再看看参数,程序是服务的提供者,那么参数作为程序的入口的携带者,一般会用来装配一些属性。

显然这里就是这样。

先看Callbacks内部关于参数部分的代码。

        // 官方注释,将配置的options由string格式转换为object格式如果需要的话// Convert options from String-formatted to Object-formatted if needed// (we check in cache first)options = typeof options === "string" ?// 注意这里, 这里去取optionsCache的值,或者调用( optionsCache[ options ] || createOptions( options ) ) :jQuery.extend( {}, options );

  在看看createOptions方法吧,其实就是个转换方法,还带有缓存功能。

// String to Object options format cache
// 建立一个缓存对象
var optionsCache = {};// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {// 创建optionsCache中的options属性var object = optionsCache[ options ] = {};// 这里用到 each方法遍历// options.split( core_rspace )  根据空格划分为数组// _在jquery中通常用来作为占位符,即忽略的参数jQuery.each( options.split( core_rspace ), function( _, flag ) {// 遍历以后将切割后的每个属性设置为trueobject[ flag ] = true;});return object;
}
// 可能例子会更清晰,
var obj = createOptions( "once memory");
/*
obj;
{once: true,memory: true
}
*/

  接下来就是具体的实现了,jQuery的实现一直是十分巧妙的,当然这可能仅仅是小菜我看来。

/** Create a callback list using the following parameters:**  options: an optional list of space-separated options that will change how*          the callback list behaves or a more traditional option object** By default a callback list will act like an event callback list and can be* "fired" multiple times.** Possible options:** once:           will ensure the callback list can only be fired once (like a Deferred)**    memory:         will keep track of previous values and will call any callback added*                    after the list has been fired right away with the latest "memorized"*                 values (like a Deferred)**  unique:         will ensure a callback can only be added once (no duplicate in the list)**  stopOnFalse:    interrupt callings when a callback returns false**///
jQuery.Callbacks = function( options ) {// Convert options from String-formatted to Object-formatted if needed// (we check in cache first)options = typeof options === "string" ?// 注意这里, 这里去取optionsCache的值,或者调用createOptions// 我们看看createOptions函数( optionsCache[ options ] || createOptions( options ) ) :jQuery.extend( {}, options );var // Last fire value (for non-forgettable lists)// 以前触发的值(为了记忆的list,记忆了上次调用时所传递的基本信息(即记忆了参数))memory,// 是否触发// Flag to know if list was already firedfired,// 是否正在触发// Flag to know if list is currently firingfiring,// 第一个被触发的function// First callback to fire (used internally by add and fireWith)firingStart,// 触发列表的长度// End of the loop when firingfiringLength,// 当前触发的索引// Index of currently firing callback (modified by remove if needed)firingIndex,// 内部存放function的数组// Actual callback listlist = [],// 用来存放重复调用的数组,(当Callbacks被配置了 once属性,则为false)// Stack of fire calls for repeatable listsstack = !options.once && [],// 内部触发函数,这里看到jquery隐藏信息的习惯了// 作为该模块的核心方法// 它没有暴露给外部,// 《代码大全》 有提到信息隐藏的好处。// Fire callbacksfire = function( data ) {// 在设置memory的情况下为 传递过来的参数data, 否则为undefinedmemory = options.memory && data;// 进入到这时标记已触发fired = true;// 当前触发索引设置为开始,或者0firingIndex = firingStart || 0;firingStart = 0;firingLength = list.length;firing = true;// for循环触发list中的函数for ( ; list && firingIndex < firingLength; firingIndex++ ) {// 如果stopOnFalse被设置,则检查调用函数后是否返回false// 如果返回则终止触发,// 注意触发参数 为一个多维数组// data = [// context,//  [args]//]  这应该是由外部封装成固定格式,再传递过来的参数if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {memory = false; // To prevent further calls using addbreak;}}// 设置正在触发为falsefiring = false;// 如果list是存在的,即改callbacks还没有被禁用if ( list ) {// 如果 stack中有值,则递归调用// 其实这里是判断是否设置了once属性if ( stack ) {if ( stack.length ) {fire( stack.shift() );}} else if ( memory ) { // 如果设置记忆功能,则清空list(注意,是记忆需要调用的基本信息,即相关参数)list = [];} else {// 只能调用一次,且不能使用memory,// 则禁用self.disable();}}},// 再来看看需要暴露的对象// Actual Callbacks objectself = {// 添加方法// Add a callback or a collection of callbacks to the listadd: function() {// list其实是可以作为是否禁用的标志的,// 如果list存在if ( list ) {// First, we save the current lengthvar start = list.length;// 真正的添加行为// 用到了自执行// 但又不是匿名函数,因为它可能需要递归(function add( args ) {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 recursivelyadd( arg );}});})( arguments );// Do we need to add the callbacks to the// current firing batch?// 如果正在触发,则只需要更新firingLengthif ( 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// 删除方法,遍历删除指定的方法,并维护好firingLength以及firingIndexremove: function() {if ( list ) {jQuery.each( arguments, function( _, arg ) {var index;while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {list.splice( index, 1 );// Handle firing indexesif ( firing ) {if ( index <= firingLength ) {firingLength--;}if ( index <= firingIndex ) {firingIndex--;}}}});}return this;},// Control if a given callback is in the list// 是否包含has: function( fn ) {return jQuery.inArray( fn, list ) > -1;},// Remove all callbacks from the listempty: function() {list = [];return this;},// Have the list do nothing anymoredisable: function() {list = stack = memory = undefined;return this;},// Is it disabled?disabled: function() {// 看,这里有用到list是否存在来判断 是否被禁用return !list;},// Lock the list in its current state// 锁住即不能再被触发// 如果没有设置memory则直接禁用lock: function() {stack = undefined;if ( !memory ) {self.disable();}return this;},// 是否加锁// Is it locked?locked: function() {// 居然是判断stack是否存在// 由此推断 加锁应该是设置智能触发一次return !stack;},// Call all callbacks with the given context and argumentsfireWith: function( context, args ) {args = args || [];// 看这封装了arguments,用来内部fire函数的调用args = [ context, args.slice ? args.slice() : args ];// 如果还没被触发,或者允许触发多次if ( list && ( !fired || stack ) ) {// 正在触发,则添加到stack// 在当次触发后,直接触发if ( firing ) {stack.push( args );} else {// 直接触发fire( args );}}return this;},// Call all the callbacks with the given arguments// 设置context为thisfire: function() {self.fireWith( this, arguments );return this;},// To know if the callbacks have already been called at least oncefired: function() {return !!fired;}};// 注意有一个细节,self的所有方法都是返回的this// 这表明,它是支持链式操作的// jquery 很多地方用了这种优雅的技术return self;
};

  好吧,Callbacks就讲到这里了,神奇而低调的函数队列,在以后的源码中你也会经常看到他的身影,所以他能做什么并不用着急。

但还是举些小例子用用看:

var c = $.Callbacks("once memory");
c.add(function(i) {alert(123 + '-' + i);
});
c.add(function(i) {alert(234 + '-' + i);
});
c.add(function(i) {alert(456 + '-' + i);
});
c.fire('tianxia');
// alert('123-tianxi'); alert('234-tianxi'); alert('456-tianxi');
c.fire();
// 再次调用,啥都没发生,因为设置了once
// 什么都没发生
c.add(function(i) {alert(i);
});
// alert('tianxia')
// 在设置memory,添加后,直接触发

  

转载于:https://www.cnblogs.com/w2154/p/4570599.html

jquery源码之低调的回调函数队列--Callbacks相关推荐

  1. jQuery源码研究分析学习笔记-回调函数(11)

    回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针调用它所指向的函数时,我们就说这是回调函数.回调函数不是由该函数的实现方直接调用,而是在特定的事件 ...

  2. jQuery源码分析系列

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

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

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

  4. jQuery源码分析-each函数

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

  5. jquery源码分析(四)——回调对象 Callbacks

    借用百度百科来说明下回调函数: 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数.回调函数不是由该 ...

  6. jQuery源码学习之Callbacks

    jQuery源码学习之Callbacks jQuery的ajax.deferred通过回调实现异步,其实现核心是Callbacks. 使用方法 使用首先要先新建一个实例对象.创建时可以传入参数flag ...

  7. Jquery源码分析-整体结构

    最近在学习Jquery的最新的源码,Jquery-3.3.1版本.网上有很多对jquery解析的文章.但是我还是要自己去尝试着看一篇jquery的源码.本系列博客用来记录其中的过程,并同大家分享.本次 ...

  8. jquery源码 DOM加载

    jQuery版本:2.0.3 DOM加载有关的扩展 isReady:DOM是否加载完(内部使用) readyWait:等待多少文件的计数器(内部使用) holdReady():推迟DOM触发 read ...

  9. jquery源码解析:代码结构分析

    本系列是针对jquery2.0.3版本进行的讲解.此版本不支持IE8及以下版本. (function(){ (21, 94)     定义了一些变量和函数,   jQuery = function() ...

最新文章

  1. C#下的Windows服务通用壳程序(二)
  2. 迭代器模式coding
  3. 采用lamp架构搭建discuz论坛
  4. 【总结】字符串匹配: KMP 和 拓展KMP
  5. html5 调用微信分享,HTML5教程之微信调用分享接口
  6. SQL Prompt 插件
  7. php编写一个学生类_PHP 结合 Boostrap 结合 js 实现学生列表删除编辑及搜索功能
  8. 《数学之美》—矩阵运算和文本处理中的两个分类问题
  9. DBeaver 连接 Oracle
  10. r语言如何下载carzip包本地安装_R语言中如何在Mac下快速下载和安装包
  11. 【NLP】基于神经网络PCNN(Piece-Wise-CNN)的关系抽取模型
  12. 树莓派 40pin IO引脚测试
  13. 上海电信宽带自助修复服务器,家庭网络故障自助修复法
  14. 热烈祝贺中贝通集团和武汉鑫炀科技顺利通过CMMIV2.0三级认证
  15. 如何生成EAN13流水号条形码
  16. 云栖独栋别墅_绿野云溪花海独栋别墅
  17. Python 3.6 中使用pdfminer解析pdf文件
  18. 如何成为一个区块链开发人员_成为开发人员是社会工作
  19. rpcs3计算机丢失,【小白必看】关于缺失Vulkan无法打开RPSC3等问题的方案
  20. 权限和归属——基本权限和特殊权限

热门文章

  1. Confluence 6 在升级过程中查看合并日志
  2. 2021年4月12日-民航上海医院-瑞金医院古北分院-检查报告单
  3. python中@wraps的作用
  4. datatable移动一列的位置
  5. 关于辅酶Q10的相关常识与选购要点(转)
  6. mesh threejs 属性_threeJS创建mesh,创建平面,设置mesh的平移,旋转、缩放、自传、透明度、拉伸...
  7. java高校教师工作量管理系统_基于ssh/bs/java/asp.net/php/web/安卓的高校教师工作量管理系统...
  8. sql 查出一张表中重复的所有记录数据
  9. video 标签存在的一些坑
  10. 口无遮拦的钉钉与坐立不安的腾讯