首页 > 代码库 > jquery的回调对象Callbacks详解

jquery的回调对象Callbacks详解

Callbacks : 对函数的统一管理

Callbacks的options参数接受4个属性,分别是
once : 只执行一次
momery : 记忆
stopOnFalse : 强制退出循环
unique : 唯一

暂时先不管4个属性有什么意思,我们看代码开始部分对options做了处理,如果options是字符串则调用createOptions方法转成json
比如:var cb = $.Callbacks(‘once momery‘);
转换为:{once:true,momery:true}
这里就不贴createOptions的源码了,大家可以自己去看一下。

jQuery.Callbacks = function( options ) {    // Convert options from String-formatted to Object-formatted if needed    // (we check in cache first)    options = typeof options === "string" ?        ( optionsCache[ options ] || createOptions( options ) ) :        jQuery.extend( {}, options );


处理完options后定义了一堆变量和方法:

var // Last fire value (for non-forgettable lists)        memory,        // Flag to know if list was already fired        fired,        // Flag to know if list is currently firing        firing,        // First callback to fire (used internally by add and fireWith)        firingStart,        // End of the loop when firing        firingLength,        // Index of currently firing callback (modified by remove if needed)        firingIndex,        // Actual callback list        list = [],        // Stack of fire calls for repeatable lists        stack = !options.once && [],        // Fire callbacks        fire = function( data ) {                    ..................                },                self = {                    add: function() {                        ..............                    },                    remove: function() {                        ................                    },                    has: function( fn ) {                        ..................                    },                    empty: function() {                        ..................                    },                    disable: function() {                        ..................                    },                    disabled: function() {                            ................                    },                    lock: function() {                        ..............                    },                    locked: function() {                        ..............                    },                    fireWith: function( context, args ) {                        ...................                    },                    fire: function() {                        ................                    },                    fired: function() {                        ................                    }                };

最后Callbacks返回了self对象,所以在self上面定义的仅供Callbacks内部使用。

内部方法中有2个比较重要,一个是list,所有add添加的函数都存放在list中,另一个是fire方法,它实现了触发list中的函数的具体逻辑。稍后我会重点讲一下它。

接下来我们分析一下self方法:

add : 添加函数
remove : 删除函数
has : 检测list中是否有相同的函数
empty : 情空list
disable : 禁止所有操作 list = stack = memory = undefined
disabled : list是否可用
lock : 锁
locked :锁是否可用
fireWith : 为触发list中的函数做预处理,最终调用fire方法
fire : 调用fireWith方法
fired : fire方法是否运行过

通过属性的名称不难发现,disabled、locked、fired这3个方法返回的是状态,代码也比较简单,不多做解释相信大家也能看懂。

disabled: function() {    return !list;},locked: function() {    return !stack;},fired: function() {    return !!fired;}

fire方法也比较简单,它调用了fireWith后返回自身,方便链式调用。

fire: function() {    self.fireWith( this, arguments );    return this;},

fireWith方法也不算太复杂,它有2个参数:
context : 执行上下文
              我们看fire方法:self.fireWith( this, arguments );
              是不是就很清楚啦,context就是self对象,args就是arguments
args : arguments

然后对args做了处理。把context与args合并在一个数组中。有经验的朋友应该不难看出,这种形式的数组是给apply用的,没错,私有的fire方法中会用到它。

if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {

 

接着往下看,if ( list && ( !fired || stack ) )  这个判断主要是为了once也就是只fire一次。

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;},

我对这个判断语句拆分一下,方便理解,list就不说了,主要说后面2个:
首次执行fireWith,因为fired是undefined,在这里取反所以为真,确保fire方法至少执行一次,然后在私有fire方法中赋值为true,下一次再执行到这里取反,则为假。

fire = function( data ) {    memory = options.memory && data;    fired = true;    firingIndex = firingStart || 0;    firingStart = 0;        ......................

看到这里相信大家明白了,要实现once必须stack为假才可以。

stack = !options.once && [],

没有设置options.once,取反为真,则stack为空数组,否则stack等于false

接下去又是一个判断:
if ( firing ) {
          stack.push( args );
} else {
          fire( args );
}

firing是为了避免死循环,当循环内需要执行的函数还没走完,则stack.push( args );

firing = true;    for ( ; list && firingIndex < firingLength; firingIndex++ ) {        if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {            memory = false; // To prevent further calls using add            break;        }    }firing = false;

当循环走完,检测到stack有长度则再调用fire

if ( list ) {    if ( stack ) {        if ( stack.length ) {            fire( stack.shift() );        }    } else if ( memory ) {        list = [];    } else {        self.disable();    }}

再看add方法,先是一个函数自执行,接收的参数是arguments,然后遍历arguments,判断如果是函数则list.push(arg),否则调用自己add(arg),由此可以看出,add方法不仅可以传一个函数,还能多个函数逗号隔开,如:cb.add(fn1,fn2);

add: function() {    if ( list ) {        // First, we save the current length        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 ) ) {                        list.push( arg );                    }                } else if ( arg && arg.length && type !== "string" ) {                    // Inspect recursively                    add( arg );                }            });        })( arguments );        // Do we need to add the callbacks to the        // current firing batch?        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 );        }    }    return this;},

else if ( memory )  当有记忆功能的时候执行,firingStart = start把循环的起始值设为当前数组的长度值,然后调用fire则只会触发当前添加的函数


私有方法fire定义了索引值、起始值、长度,就开始循环,如果触发的函数返回false,并且options.stopOnFalse为true,则终止循环。

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; // To prevent further calls using add            break;        }    }    firing = false;    if ( list ) {        if ( stack ) {            if ( stack.length ) {                fire( stack.shift() );            }        } else if ( memory ) {            list = [];        } else {            self.disable();        }    }},

 

读了一遍源码后,我们应该对使用Callbacks非常熟悉了:

//oncevar cb = $.Callbacks(‘once‘);cb.add(function(){    alert(‘a‘);});cb.add(function(){    alert(‘b‘);});cb.fire();//弹出a,bcb.fire();//不执行//memoryvar cb = $.Callbacks(‘memory‘);cb.add(function(){    alert(‘a‘);});cb.add(function(){    alert(‘b‘);});cb.fire();//弹出a,bcb.add(function(){ //弹出c    alert(‘c‘);});//once memoryvar cb = $.Callbacks(‘once memory‘);cb.add(function(){    alert(‘a‘);});cb.add(function(){    alert(‘b‘);});cb.fire();//弹出a,bcb.add(function(){ //弹出c    alert(‘c‘);});cb.fire(); //不执行//add方法多个参数逗号隔开var cb = $.Callbacks();cb.add(function(){    alert(‘a‘);},function(){    alert(‘b‘);});cb.fire(); //弹出a,b//stopOnFalsevar cb = $.Callbacks(‘stopOnFalse‘);cb.add(function(){    alert(‘a‘);    return false;},function(){    alert(‘b‘);});cb.fire();//弹出a//lock()var cb = $.Callbacks(‘memory‘);cb.add(function(){    alert(‘a‘);});cb.fire();//弹出acb.lock();//锁住fire()cb.add(function(){ //弹出b    alert(‘b‘);});cb.fire();//不执行//remove()var cb = $.Callbacks();var fn1 = function(){    alert(‘a‘);};var fn2 = function(){    alert(‘b‘);};cb.add(fn1);cb.add(fn2);cb.fire(); //弹出a,bcb.remove(fn1,fn2);cb.fire();//不执行

 

jquery的回调对象Callbacks详解