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非常熟悉了:

//once
var cb = $.Callbacks(‘once‘);
cb.add(function(){
    alert(‘a‘);
});
cb.add(function(){
    alert(‘b‘);
});
cb.fire();//弹出a,b
cb.fire();//不执行

//memory
var cb = $.Callbacks(‘memory‘);
cb.add(function(){
    alert(‘a‘);
});
cb.add(function(){
    alert(‘b‘);
});
cb.fire();//弹出a,b

cb.add(function(){ //弹出c
    alert(‘c‘);
});

//once memory
var cb = $.Callbacks(‘once memory‘);
cb.add(function(){
    alert(‘a‘);
});
cb.add(function(){
    alert(‘b‘);
});
cb.fire();//弹出a,b

cb.add(function(){ //弹出c
    alert(‘c‘);
});
cb.fire(); //不执行

//add方法多个参数逗号隔开
var cb = $.Callbacks();
cb.add(function(){
    alert(‘a‘);
},function(){
    alert(‘b‘);
});

cb.fire(); //弹出a,b

//stopOnFalse
var 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();//弹出a
cb.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,b

cb.remove(fn1,fn2);
cb.fire();//不执行
时间: 2024-10-17 06:33:10

jquery的回调对象Callbacks详解的相关文章

jarson jQuery的deferred对象使用详解——实现ajax线性请求数据

最近遇到一个ajax请求数据的问题 ,就是想要请求3个不同的接口,然后请求完毕后对数据进行操作,主要问题就是不知道这3个请求誰先返回来,或者是在进行操作的时候不能保证数据都已经回来,首先想到能完成的就是在第一个ajax请求的succes里面写第二个ajax请求,但是大家都知道,这样写的效率会很低很低,所以放弃了. 查阅了很久,终于找到了方法.使用deferred对象. jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本.每个版本都会引入一些新功能.今天我想介绍的,就是从jQue

jQuery的deferred对象使用详解——实现ajax线性请求数据

最近遇到一个ajax请求数据的问题 ,就是想要请求3个不同的接口,然后请求完毕后对数据进行操作,主要问题就是不知道这3个请求誰先返回来,或者是在进行操作的时候不能保证数据都已经回来,首先想到能完成的就是在第一个ajax请求的succes里面写第二个ajax请求,但是大家都知道,这样写的效率会很低很低,所以放弃了. 查阅了很久,终于找到了方法.使用deferred对象. jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本.每个版本都会引入一些新功能.今天我想介绍的,就是从jQue

jQuery的回调对象Callbacks分析

Callbacks在jQuery中的地位是Deferred的基础,当然,也对外公开其方法. Callbacks对象是统一管理多个函数的利器.其核心是参数options中的几个状态码的任意组合,非常灵活. 先来看看options once: 只触发一次回调 memory: 如果是触发后状态,add后立即触发 unique: 添加相同回到函数,只触发其中一个 stopOnFalse: 回调中遇到return false, 后面都不执行. 首先是: list = [] 我们的工作就是在维护这个list

jquery源码解析:jQuery静态属性对象support详解

jQuery.support是用功能检测的方法来检测浏览器是否支持某些功能.针对jQuery内部使用. 我们先来看一些源码: jQuery.support = (function( support ) { ...... return support;})( {} ); jQuery.support其实就是一个json对象.在火狐浏览器下,打印出support对象: 接下来,我们来看它的源码 jQuery.support = (function( support ) { var input = d

jQuery回调、递延对象总结(一)jQuery.Callbacks详解

前言: 作为参数传递给另一个函数执行的函数我们称为回调函数,那么该回调又是否是异步的呢,何谓异步,如:作为事件处理器,或作为参数传递给 (setTimeout,setInterval)这样的异步函数,或作为ajax发送请求,应用于请求各种状态的处理,我们可以称为异步回调,jQuery.Callbacks 为我们封装了一个回调对象模块,我们先来看一个应用场景: // 为什么jQuery中的ready事件可以执行多个回调,这得益于我们的jQuery.Deferred递延对象(是基于jQuery.Ca

关于Defferred对象知识详解

关于Defferred对象知识详解 一.什么是deferred对象 Deferred是jQuery开发团队为延时操作做出的回调函数的解决方案,意思是延时到某个时间点再执行. 二.deferred的实现 1.创建三个$.Callbacks对象,分别表示成功done,失败fail,处理中process三种状态 2.对应了三种处理结果,resolve(已完成).rejiect(以失败).notify(未完成) 3.创建一个promise对象,具有state.always.then.primise方法

JQuery中$.ajax()方法参数详解

url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如put和 delete也可以使用,但仅部分浏览器支持. timeout: 要求为Number类型的参数,设置请求超时时间(毫秒).此设置将覆盖$.ajaxSetup()方法的全局设 置. async:要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求. 如果需要发送同步请求,请将此选项

JQuery中$.ajax()方法参数详解 (转)

url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如put和 delete也可以使用,但仅部分浏览器支持. timeout: 要求为Number类型的参数,设置请求超时时间(毫秒).此设置将覆盖$.ajaxSetup()方法的全局设 置. async:要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求. 如果需要发送同步请求,请将此选项

jQuery.extend()、jQuery.fn.extend()扩展方法详解

jQuery自定义了jQuery.extend()和jQuery.fn.extend()方法.其中jQuery.extend()方法能够创建全局函数或者选择器,而jQuery.fn.extend()方法能够创建jQuery对象方法. 例如: jQuery.extend({ showName : function(name){ alert(name) } }); jQuery.showName("深蓝"); jQuery.extend()除了可以创建插件外,还可以用来扩展jQuery对象