代码改变世界

jquery1.8.3 callbacks源码分析

2012-12-04 09:49  飞魚  阅读(1044)  评论(0编辑  收藏  举报

先附上源码如下

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时,

if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {//走了这里,list重置
list = [];
} else {
self.disable();
}
}
在下一次add时,
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 );
}
当你在fire时,list为[]所以什么都不执行,再次充值list=[]

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...