今天读了下《jquery 技术内幕》这本书 里边jquery.Callbacks()讲解的非常好,尤其是各种例子,这里贴出注释,和书里的测试代码,拿着测试代码对照着注释一步一步的调试Callbacks就能比较深刻的理解jQuery里回调函数列表的管理是怎么设计的。
代码注释:
//jQuery Callbacks
/*
jQuery 的Callbaks 是jQuery根据不同的参数来管理回调函数列表的
基础函数,他为jQuery中的$.ajax()和$.Deferred()函数提供基础支持,它
提供add(),fire(),remove(),disabled()等功能
参数 flags:空格分割的改变回调函数行为的参数
默认情况下一个回调函数列表就像一组事件性质的回调函数列表
可能的参数
flags的可能值:
once: 回调函数列表只触发一次
memory:记录上一次回调函数参数,之后的回调函数添加就会立即使用记录的参数立刻调用
unique: 保证一个回调函数只加入列表一次,不能有重复的函数
stopOnFalse:打断回调函数列表 当回调函数返回 false
jquery中 用了 once memoery的参数组合表示函数只触发一次,以后添加的函数会已记录的参数,立即执行
*/
function createFlags( flags ){
var object = flagsCache[ flags ] = {}, //注意这个技巧,object和flagsCache flagsCache[flags]指向同一个空的对象,为object添加属性就是为flagsCache[flags]添加属性
i,length;
flags = flags.split(/\s+/);
for( i = 0,length = flags.length; i < length; i++){
object[flags[i]] = true;
}
flagsCache[]
}
jQuery.Callbacks = function(flags){
//把字符串的参数 变为对象类型的参数 首先从缓存中去取
flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
var
list = [], //回调函数列表
stack = [], //当函数正在fire中,执行中时,把函数加入stack 等待执行
memory,
//是否正在触发中
firing = false,
//开始fire的函数下标
firingStart,
//需要fire函数列表的长度
firingLength,
//正在fire函数的下标
firingIndex,
add = function(args){
var i,
length,
elem,
type;
for(i = 0,length = args.length; i < length){
elem = args[i];
type = jQuery.type(elem);
if(type === "array"){
//处理递归
add(elem);
}else if(type === "function"){
//注意这里的这个技巧 如果是unique模式,就去用has检查,用|| 加 ! 而不是用if else
if(!flags.unique || !self.has(elem)){
list.push(elem);
}
}
}
},
//触发 callbacks
fire = function(context,args){
args = args || [];
/*
如果memory undefined 表示函数列表未被触发过 或者 已经被禁用
在memory模式下 memory的值为[context,args] 间接表示回调函数列表被触发过
非memory模式下 true 间接表示回调函数列表已经被触发过 once模式下不再触发
stopOnFalse模式下 返回false 则memory为true 表示回调函数列表已经被处触发过
*/
memory = !flags.memory || [ context, args ];
firing = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
for(;list && firingIndex < firingLength; firingIndex++){
if(list[firingIndex].apply(context,args) === false && flags.stopOnFalse){ //执行函数
memory = true;
break;
}
}
firing = false; //触发结束
//触发结束
if( list ){
if(!flags.once){
//对于在 上边for循环中【list[firingIndex].apply(context,args)】产生的fire 会在这里处理
if(stack && stack.length){
memory = stack.shift(); //取数组
self.firWith(memory[0],memory[1]);
}
}else if( memory === true){
self.disable();
}else{
list = [];
}
}
},
// 返回的 Callbacks 对象
self = {
add:function(){
if(list){
var length = list.length;
add(arguments);
//哈哈,这里的注释是 我们需要把函数加到目前正在触发的队列么???
if(firing){
firingLength = list.length;
//是 memory 模式 并且 还没有 stopOnFalse
}else if(memory && memory !== true){
firingStart = length;
fire(memory[0],memory[1]); //立即执行
}
}
return this;
},
remove:function(){
if(list){
var args = arguments,
argsIndex = 0,
argsLength = arguments.length;
for(; argsIndex < argsLength; argsIndex++){
for(var i = 0; i < list.length; i++){
if(args[argsIndex] === list[i]){
//处理触发中的remove 改变firingIndex和firingLength
if( firing ){
if( i < firingLength ){
firingLength--; //firingIndex 长度减一
if( i <= firingIndex ){//目前执行中的函数为第2个,remove的是第4个跳过若remove的是第1个,firingIndex向前移动一个
firingIndex--;
}
}
}
//从list列表中删除
list.splice(i--,1);
if(flags.unique){
break
}
}
}
}
}
return this;
},
//判断函数是否已经在函数列表中
has:function(fn){
if(list){
var i = 0,length = list.length;
for(; i < length; i++){
if(fn === list[i]){
return true;
}
}
return false;
}
},
//彻底销毁回调函数列表
disable: function(){
list = stack = memory = undefined;
return this;
},
//判断是否disable
disabled:function(){
return !list;
},
lock:function(){
stack = undefined; //禁止了回调函数fire
if( !memory || memory === true){
self.disable();
}
}
//从列表清除所有的回调函数
empty:function(){
list = [];
return this;
}
//根据给定的上下文和参数来触发回调函数
fireWith:function( context, args){
if(firing){
if(!flags.once){
stack.push([context,args]); //如果正在函数执行过程中又产生触发 则会先存入stack
}
}else if( !(flags.once && memory)){
fire(context,args);
}
return this;
},
fire:function(){
self.fireWith(this,arguments);
return this;
},
};
}
测试代码:
<script>
/*
var callbacks = jQuery.Callbacks(),
something = true;
function fn1(args){
console.log('fn1',args);
if(something){
callbacks.fire("测试:执行过程中,第二次触发");
callbacks.fire("测试:执行过程中,第三次触发");
something = false;
}
}
callbacks.add(fn1);
callbacks.fire('测试:第1次触发'); //多次触发
*/
/*
var callbacks = jQuery.Callbacks('once');
function fn1(args){
console.log('fn1',args);
}
function fn2(args){
console.log('fn2',args);
}
callbacks.add(fn1);
callbacks.add(fn2);
callbacks.fire('测试once模式');
console.log('是否已被禁用?',callbacks.disabled());
*/
/*
var callbacks = jQuery.Callbacks('once stopOnFalse');
function fn1(args){
console.log("fn1",args);
return false;
}
function fn2(args){
console.log('fn2',args);
}
callbacks.add(fn1);
callbacks.add(fn2);
callbacks.fire("测试:once+stopOnFalse模式");
console.log('是否被禁用?',callbacks.disabled());
*/
/*
var callbacks = jQuery.Callbacks('once memory');
function fn1(args){
console.log('fn1',args);
}
function fn2(args){
console.log('fn2',args);
}
callbacks.add(fn1);
callbacks.fire('测试 once模式 + memory 模式');
callbacks.add(fn2);
*/
/*
测试 add 中 if(fireing){ 调整了 firingLength 会立即执行
*/
/*
var callbacks = jQuery.Callbacks();
function fn1(args){
console.log('fn1',args);
console.log('fn1 before add fn2');
callbacks.add(fn2);
console.log('fn1 after add fn2');
}
function fn2(args){
console.log('fn2',args);
}
callbacks.add(fn1);
callbacks.fire("测试正在执行中 添加回调函数");
*/
/*
remove 的测试
*/
var callbacks = jQuery.Callbacks();
function fn1(args){
console.log('fn1',args);
}
function fn2(args){
console.log('fn2',args);
console.log('before remove fn1');
callbacks.remove(fn1);
console.log('after remove fn1');
console.log('before remove fn3');
callbacks.remove(fn3);
console.log('after remove fn3');
}
function fn3(args){
console.log('fn3',args);
}
callbacks.add(fn1);
callbacks.add(fn2);
callbacks.add(fn3);
callbacks.fire('测试 移除毁掉函数,正在执行中,待移除函数已执行');
</script>