jQuery的回调管理机制
// 对option的一个缓存,避免每次都需要createOptions,option是创建Callback对象时的传入的参数
// 每个option被存入optionsCache中类似于{memory:true, once:true,...}
var optionsCache = {};
// 将字符串表达转成对象表达,并存在缓存中,一个内部使用的方法。
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
}
/*
* Callback构造函数在构造对象时的一些参数选项
*
* once: 确保这个回调列表只执行一次(像一个递延 Deferred).
* memory: 保持以前的值和将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred).
* unique: 确保一次只能添加一个回调(不能列表中的重复).
* stopOnFalse: 当一个回调返回false 时中断调用
*/
// options参数以字符串的形式传入,例如"unique memory",以空格分割,用来控制回调函数的行为
// $.Callbacks是在jQuery内部使用,如为$.ajax,$.Deferred等组件提供基础功能的函数。它也可以用在类似功能的一些组件中,如自己开发的插件。
jQuery.Callbacks = function( options ) {
options = typeof options === "string" ?
// 先检测一下cache,如果为空,则把options从字符串的形式转变为Object的形式
// 通过字符串在optionsCache寻找有没有相应缓存,如果没有则创建一个,有则引用
( optionsCache[ options ] || createOptions( options ) ) :
// 如果是对象则通过jQuery.extend扩展到新对象中赋值给options
jQuery.extend( {}, options );
var
// 最后一次触发回调时传的参数
memory,
// 列表中的函数是否已经回调至少一次
fired,
// 列表中的函数是否正在回调中
firing,
// 回调的起点
firingStart,
// 回调时的循环结尾
firingLength,
// 当前正在回调的函数索引
firingIndex,
// 回调函数列表
list = [],
// 可重复的回调函数堆栈,用于控制触发回调时的参数列表
stack = !options.once && [],
// 触发回调函数列表
//这里的data参数到底是啥???
fire = function( data ) {
//如果参数memory为true,则记录data
memory = options.memory && data;
//标记触发回调
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
//标记正在触发回调
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
// 对当前回调函数在data[0]环境下,传入data[1]参数,并执行,如果返回false,并且设置了stopOnFalse,则将memory设为false并返回。
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
// 阻止未来可能由于add所产生的回调,因为当memory为true时,add进来的回调函数会立即被触发
memory = false;
//由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环
break;
}
}
//标记回调结束
firing = false;
//如果列表存在
if ( list ) {
//如果堆栈存在
if ( stack ) {
//如果堆栈不为空
if ( stack.length ) {
//从堆栈头部取出,递归fire。
fire( stack.shift() );
}
//否则,如果有记忆
} else if ( memory ) {
//列表清空
list = [];
//再否则阻止回调列表中的回调
} else {
self.disable();
}
}
},
// Callback构造函数返回的对象
self = {
// 向回调列表中添加一个回调或回调的集合。
add: function() {
if ( list ) {
// 首先我们存储当前列表长度 主要用于当Callbacks传入memory参数时
var start = list.length;
// 在做其它操作之前,先对args做一些处理
(function add( args ) {
// 对args传进来的列表的每一个对象执行操作
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
// 如果是函数
if ( type === "function" ) {
// 如果回调离别不允许重复,则看看列表中是否包含,如果不包含则加入列表
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
// 如果是类数组或对象,则递归调用add方法
} else if ( arg && arg.length && type !== "string" ) {
//递归 调用add方法往list中增加回调
add( arg );
}
});
})( arguments );
// 如果整个callbacks对象中的callback正在执行时,回调时的循环结尾变成add之后的list长度长度,确保新增加的回调函数会被执行到
if ( firing ) {
firingLength = list.length;
// 如果有memory,我们立刻调用。 这里的memory
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// 从列表删除回调函数
remove: function() {
if ( list ) {
//继续用jQuery.each,对arguments中的所有参数处理
jQuery.each( arguments, function( _, arg ) {
var index;
//找到arg在列表中的位置
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
//根据得到的位置删除列表中的回调函数
list.splice( index, 1 );
if ( firing ) {
//如果正在回调过程中,则调整循环的索引和长度
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// 判断回调函数是否在列表中
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// 从列表中删除所有回调函数
empty: function() {
list = [];
firingLength = 0;
return this;
},
// 禁用回调列表中的回调。禁用之后无法再向列表中加入回调函数
disable: function() {
list = stack = memory = undefined;
return this;
},
// 判断列表是否被禁用
disabled: function() {
return !list;
},
// 锁定列表
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
// 判断列表是否被锁
locked: function() {
return !stack;
},
// 以给定的上下文和参数调用所有回调函数
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;
},
// 以给定的参数调用所有回调函数
fire: function() {
self.fireWith( this, arguments );
return this;
},
// 回调函数列表是否至少被调用一次
fired: function() {
return !!fired;
}
};
return self;
};
jQuery的回调管理机制的更多相关文章
- jQuery的回调管理机制(三)
jQuery.when()方法是jQuery内部使用回调机制的范例. // 参数为多个方法,这些方法全部执行完成之后执行回调 when: function( subordinate /* , ..., ...
- jQuery的回调管理机制(二)
jQuery.extend({ /* * deferred对象的一大好处,就是它允许你自由添加多个回调函数. * $.ajax("test.html") .done(func ...
- ARC内存管理机制详解
ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数. ...
- Android包管理机制(二)PackageInstaller安装APK
前言 在本系列上一篇文章Android包管理机制(一)PackageInstaller的初始化中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInsta ...
- Linux中断管理 (1)Linux中断管理机制
目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管 ...
- untiy3d action管理机制的编写
使用unity3d对于一些可视化强迫者来说,是一个不错的选择,但unity3d没有cocos2d的action管理机制,比如cocos2dx的CCMoveTo,CCScale等action,所以笔者通 ...
- Android开发——Android 6.0权限管理机制详解
.Android 6.0运行时主动请求权限 3.1 检测和申请权限 下面的例子介绍上面列出的读写SD卡的使用例子,可以使用以下的方式解决: public boolean isGrantExterna ...
- Linux中断管理 (1)Linux中断管理机制【转】
转自:https://www.cnblogs.com/arnoldlu/p/8659981.html 目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机 ...
- Android中的内存管理机制以及正确的使用方式
概述 从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源.现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操 ...
随机推荐
- 每天一个linux命令:wc命令
Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数.字数.行数,并将统计结果显示输出. 1.命令格式: wc [选项]文件... 2.命令功能: 统计指定文件中的字节数. ...
- http.sys的简单应用
//public void Run() //{ // //httpListener提供一个简单,可通过编程方式控制的Http协议侦听器.此类不能被继承. // if (!HttpListener.Is ...
- 开源项目推荐:e-example / Springboot+bootstrap + ……
前言: 我想要找一个 springboot + bootstrap 的例子介绍,然后搜索到了这个开源项目. 所有能跑起来的项目都有研究价值,看看这个项目的文档.目前正好满足我想要的功能.推荐 正文: ...
- Netbeans配置Java SE嵌入式平台(树莓派)
1.配置平台 来源:http://www.cnblogs.com/xiaobo-Linux/p/7955769.html 2. 选择远程 3. 填写信息 JRE 路径是树莓派上的路径:how? sud ...
- Android Studio 项目中,哪些文件应该忽略而不提交到svn的服务器中?
Android Studio 中建议过滤的文件: - .idea 文件夹 - .gradle 文件夹 - 所有的 build 文件夹 - 所有的 .iml 文件 - local.properties ...
- Android OpenGL ES 离屏渲染(offscreen render)
通常在Android上使用OpenGL ES,都是希望把渲染后的结果显示在屏幕上,例如图片处理.模型显示等.这种情况下,只需要使用Android API中提供的GLSurfaceView类和Rende ...
- Chrome扩展应用
现在越来越多的用户将chrome浏览器设置为自己默认的浏览器,不仅是因为他的界面美,最重要的是他对html5和CSS3完美的支持,且调试工具非常好用,还有丰富的扩展库.如何安装自己的扩展呢? 点击自定 ...
- mac版本cornerstone的无限期破解方法(转)
CornerStone是个人非常喜欢的mac上的一款SVN客户端工具,官方提供了14天的免费试用(trail)版本.我们可以在此基础上提供无限期试用版本. 方法一:如果你从来没有安装过这个trail版 ...
- 为什么调用 FragmentPagerAdapter.notifyDataSetChanged() 并不能更新其 Fragment?【转载】
转载自:http://www.cnblogs.com/dancefire/archive/2013/01/02/why-notifyDataSetChanged-does-not-work.html ...
- 第四章 TCP粘包/拆包问题的解决之道---4.1---
4.1 TCP粘包/拆包 TCP是一个“流”协议,所谓流,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可 ...