概述
本文详细分析jquery-1.11.1.js源码文件行数:3048~3248;
代码简介:定义了工具函数callbacks,用于创建Callbacks对象,Callbacks对象可添加异步回调函数,对他们的执行进行统一管理;
下文进行详细代码分析。
jQuery.Callbacks
// 匹配非空白
var rnotwhite = (/\S+/g);
// 缓存已经建立过的参数组合
var optionsCache = {};
// 将建立Callbacks对象传入的参数String,转成对象使用并且缓存
function createOptions( options ) {
var object = optionsCache[ options ] = {};
// 使用match将options分割开,match到的非空白字符串构成数组,执行回调后转成boolean放进object
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
}
// Callbacks
jQuery.Callbacks = function( options ) {
// 将参数转成对象使用,先判断缓存中是否已有
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );
var // list里的回调是否正在执行的标志
// 个人认为,主要是考虑list里的函数可能会操作Callbacks对象本身
firing,
// 最后执行过fire的上下文和参数数据
memory,
// 判断list是否已经执行过的标志
fired,
// list的长度
firingLength,
// list正在执行的函数位置
firingIndex,
// list开始执行的位置
firingStart,
// 回调函数列表
list = [],
// 这是多用途的变量,可重复触发时,为数组,保存firing过程中,list的函数又调fire去执行的其他函数,如果设置了once,则会为false,就会用于阻止触发
stack = !options.once && [],
// 调用回调函数
fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
// list执行前设置firing状态
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
// options.stopOnFalse为true且回调返回false时,list停止
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
// 走进分支说明options.stopOnFalse为true且执行结果出现了false,设置memory,保证之后add添加进来的函数不会再触发
memory = false;
break;
}
}
// list执行完毕设置firing状态
firing = false;
if ( list ) {
// stack存在说明可重复触发
if ( stack ) {
// length大于0则说明保存了没触发的函数的参数,取出并且递归调用fire
// 由于前面firingIndex和firingLength很好的保存了list现在的已执行状态的函数的位置,因此递归调用只会执行新增的函数
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
// disable掉对象,阻止重复触发
} else {
self.disable();
}
}
},
// Callbacks实际返回的对象
self = {
// 添加回调,可传单个或多个
add: function() {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add( args ) {
//使用each对每个回调执行操作
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
// unique没设置或者设置了但arg不在list里,则可添加
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {
// arg不是function,但存在length表示类数组,则调用递归,相当于躺平数组
add( arg );
}
});
})( arguments );
// 如果list正在执行,则重设firingLength长度,因为list这时候变长了
if ( firing ) {
firingLength = list.length;
// 如果memory不为空,则需要立即执行新增的函数
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// 删除回调
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// 处理firing的场景(说明回调函数里调用了remove,避免list后面的回调发生混乱)
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// 使用inArray判断fn是否已经加进list
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// 清空所有回调
empty: function() {
list = [];
firingLength = 0;
return this;
},
// disable功能
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;
},
// 可传自定义上下文和参数去执行list里的函数
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
// 判断是否正在执行,firing为true只能说明正在执行的回调里面又对Callbacks对象进行了操作
if ( firing ) {
// 先将参数保存在stack里,最后再执行
stack.push( args );
} else {
fire( args );
}
}
return this;
},
// 调用保存起来的回调函数,传入的参数会作为回调函数的参数
fire: function() {
self.fireWith( this, arguments );
return this;
},
// Callbacks对象是否已经至少fire过一次
fired: function() {
return !!fired;
}
};
return self;
};