先附上源码如下
var optionsCache = {};//缓存options function createOptions( options ) { //接受字符串参数,如 传入"unique memory" var object = optionsCache[ options ] = {}; jQuery.each( options.split( core_rspace ), function( _, flag ) { //按空格分离字符串 object[ flag ] = true; //optionsCache["unique memory"]["unique"]=true, optionsCache["unique memory"]["memory"]=true }); return object; } //不管是否传入参数都返回内部的self对象,可以调用对象的add,fire等方法 jQuery.Callbacks = function( options ) { //判断传入参数是否为字符串,是则去optionsCache去获取,没有获取到的话则createOptions创建并返回对象,非字符串,用extend返回对象 options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var memory, fired, firing, firingStart, firingLength, firingIndex, list = [], stack = !options.once && [], fire = 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; break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, self = { add: function() { if ( list ) {//判断list是否存在 var 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 ) ) {//如果是unique唯一的,判断list数组是否存在,存在则不push list.push( arg );//为list添加函数 } } else if ( arg && arg.length && type !== "string" ) {//类数组或数组递归调用,可以看出add接受的为函数列表 add( arg ); } }); })( arguments ); if ( firing ) { firingLength = list.length; } 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 ); if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, has: function( fn ) {//判断fn是否存在list中 return jQuery.inArray( fn, list ) > -1; }, empty: function() { list = []; 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 ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( list && ( !fired || stack ) ) { if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, fire: function() {//触发callbacks self.fireWith( this, arguments ); return this; }, fired: function() { return !!fired; } }; return self; };
上面没有全写注释是因为感觉说不清楚,下面按支持的flags分几种情况
1、直接用的情况
function f1(v){
console.log("f1:"+v);
}
function f2(v){
console.log("f2:"+v);
}
function f3(v){
console.log("f3:"+v);
}
var cb=$.Callbacks();
cb.add(f1);
cb.fire("foo");//f1:foo
cb.add(f2,f3);
cb.fire("bar");//f1:bar f2:bar f3:bar
cb.fire("bar2");//f1:bar2 f2:bar2 f3:bar2
可以看出cb可以add在add,fire在fire
跟进源码分析
cb.add中把函数push进list数组,firing,memory都为false不用管,add操作ok
cb.fire调用了fireWith,可以看出fireWith可以自己绑定context,fire则默认为this
fireWith: function( context, args ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( list && ( !fired || stack ) ) { //!fired和stack均为true
if ( firing ) { //firing为false
stack.push( args );
} else {
fire( args );
}
}
return this;
}
可以看出调用了Callbacks内部的fire函数,里面循环调用list里的函数,firingIndex一直为0,所以再次add或调用,里面的list函数都重新执行一遍,接着
if ( list ) {
if ( stack ) {//这里为true
if ( stack.length ) {//为false,所以什么都不做,cb.fire操作也ok结束
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
2传入once的情况
var cb=$.Callbacks(“once”);
cb.add(f1);
cb.fire("foo");//f1:foo
cb.add(f2,f3);
cb.fire("bar");//无
cb.fire("bar2");//无
cb.add同上
cb.fire基本同上,只不过这里不同
if ( list ) {
if ( stack ) {//为false
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();//走到了这里 list = stack = memory = undefined;都设置了undefined,当你在add或fire的时候,list为undefined,所以不做任何操作
}
}
3传入memory的情况
var cb=$.Callbacks(“memory”);
cb.add(f1);
cb.fire("foo");//f1:foo
cb.add(f2,f3);//f2:foo f3:foo
cb.fire("bar")//f1:bar f2:bar f3:bar
这里与第一种情况基本相同,
if ( firing ) {
firingLength = list.length;
} else if ( memory ) {//只不过在第一次以后的add的时候,走这里
firingStart = start;//这里改变长度,为只给新添加的函数执行
fire( memory );//在add的时候把上次fire的数据传给此次add的函数执行
}
3传入unique的情况
这个比较简单,在add的时候判断添加的函数是否已经存在
if ( !options.unique || !self.has( arg ) ) {//如果是unique唯一的,判断list数组是否存在,存在则不push
list.push( arg );//为list添加函数
}
4传入stopOnFalse的情况
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//可以看到就这里有stopOnFalse 这种情况就break了
memory = false;
break;
}
5传入多个的情况
1)"unique memory"这种情况就是添加的重复的不执行
2)"memory stopOnFalse "
function f1(v){
console.log("f1:"+v);
return false
}
function f2(v){
console.log("f2:"+v);
}
function f3(v){
console.log("f3:"+v);
}
var cb=$.Callbacks("memory stopOnFalse");
cb.add(f1);
cb.fire("foo");//f1:foo
cb.add(f2,f3);//无
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//可以看到就这里有stopOnFalse 这种情况就break了
memory = false; 可以看到这里memory =false
break;
}
所以add的时候
if ( firing ) {
firingLength = list.length;
} else if ( memory ) {//不走这里
firingStart = start;
fire( memory );//不执行
}
3)"memory once"
var cb=$.Callbacks("memory once");
cb.add(f1);
cb.fire("foo");//f1:foo
cb.add(f2,f3);//f2:foo f3:foo
cb.fire("bar")//无
在第一次fire时,
6执行的时候在add或fire的情况
function f1(v){
console.log("f1:"+v);
cb.add(f3)
}
function f2(v){
console.log("f2:"+v);
}
function f3(v){
console.log("f3:"+v);
}
var cb=$.Callbacks("memory");
cb.add(f1);
cb.fire("foo");//f1:foo f3:foo
cb.add(f2);//f2:foo
你可以发现当f1执行时又cb.add了
if ( firing ) {//这时firing为true
firingLength = list.length;//加长到当前list数组长度,以便执行新添加的
} else if ( memory ) {
firingStart = start;
fire( memory );
}
至于其他组合和情况以及empty、remove、lock方法我想你能分析了...good job...