jquery中Callbacks对象的实现

冷宏茂
2023-12-01

jquery中的Callbacks方法为工具对象,它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数的队列。其实Callbacks的思想跟观察者模式的思想相似

Callbacks的用法

function aaa(m){
    alert(m);
}
function bbb(n){
    alert(n);
}
var cb = $.Callbacks();//实例化Callbacks对象
cb.add(aaa);//添加该函数到回调队列
cb.add(bbb);
cb.fire(2);//执行回调队列中的函数 执行后弹出2,2

另外创建$.Callbacks()对象时,可以传入配置参数,传入的参数可以是’once’,’memory’,’stopOnFalse’,’unique’,各个配置参数的意义如下:

  • once: fire只能触发一次,第一次之后的fire不执行 作用到fire方法中
  • memory: 可以触发所有的add 包括fire后定义的add,作用到add中
  • unique: 去重,只能执行不同的函数,作用到add中
  • stopOnFalse: 当有函数的返回值为false时,这个函数之后的函数将不会执行 作用到fire方法中

配置参数可以有组合的情况,组合的参数需要以空格分隔

var cb1 = $.Callbacks('memory')
function ccc(){
    alert(3);
}
cb1.add(ccc);

function ddd(){
    alert(4);
}
cb1.fire();//弹出3 4
cb1.add(ddd);

Callbacks对象的源码解析

Callbacks对象的公共方法如下
add(): 向回调列表中添加一个回调函数或回调的集合
disable(): 禁用回调列表中的回调
empty(): 从列表中删除所有的回调
fire():用给定的参数调用所有的回调
fired(): 确定回调列表中的回调是否已被执行
fireWith(): 访问给定上下文和参数列表的所有回调
has(): 确定列表中是否提供一个回调
lock(): 锁定当前状态的回调列表
locked(): 确定回调列表是否已被锁定
remove():从回调列表中删除一个回调或回调集合。

var rnotwhite = (/\S+/g);//匹配任何可见字符
// 如果options='memory' 返回options={memory:true} optionsCache{options:{memory:true}}
var optionsCache = {};

// Convert String-formatted options into Object-formatted ones and store in cache
//将字符串格式的options转换为object格式的options 并保存在cache中
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
    object[ flag ] = true;
});
return object;
}

jQuery.Callbacks = function( options ) {

// Convert options from String-formatted to Object-formatted if needed
// 从options中分隔参数 
// (we check in cache first)
// 兼容处理 options的值有两种类型 一种为string 另一种为undefined 当为undefined时 将undefined改为{},避免以后的兼容处理
options = typeof options === "string" ?
    ( optionsCache[ options ] || createOptions( options ) ) :
    jQuery.extend( {}, options );

var // Flag to know if list is currently firing标记list是否正在执行
    firing,
    // Last fire value (for non-forgettable lists) 如果参数中设定了memory 则记录最后一次执行callback的值,如果没设置memory,将该值设定为true
    memory,
    // Flag to know if list was already fired 标记list是否已执行
    fired,
    // End of the loop when firing 执行循环的终点
    firingLength,
    // Index of currently firing callback (modified by remove if needed)当前正在执行的callback是第几个
    firingIndex,
    // First callback to fire (used internally by add and fireWith) 第一个执行的callback索引
    firingStart,
    // Actual callback list
    list = [],
    // Stack of fire calls for repeatable lists 
    stack = !options.once && [],//当fire没有'once'限制时 stack存储在回调函数中fire的回调列表 防止出现死循环
    // Fire callbacks
    // data为数组 第一个参数为执行上下文 第二个参数为执行时的参数
    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;//触发结束
        // 触发完list后触发stack中的回调
        if ( list ) {
            if ( stack ) {
                if ( stack.length ) {
                    fire( stack.shift() );
                }
                //只执行一次时触发的操作
            } else if ( memory ) {
                list = [];//清空数组 以后执行的回调中什么都没有
            } else {
                self.disable();//禁止以后的所有fire操作
            }
        }
    },
    // Actual Callbacks object
    self = {
        // Add a callback or a collection of callbacks to the list
        // 添加回调函数
        add: function() {
            if ( list ) {
                // First, we save the current length
                // 确认存储位置,从数组的末尾添加
                var start = list.length;
                //针对add方法中传入多个参数的情况
                (function add( args ) {
                    jQuery.each( args, function( _, arg ) {
                        var type = jQuery.type( arg );
                        if ( type === "function" ) {
                            //确定参数是否具有唯一性限制
                            //如果有唯一性限制 判断函数是否已包含在list中
                            if ( !options.unique || !self.has( arg ) ) {
                                list.push( arg );
                            }
                            //add([aaa,bbb])情况
                        } else if ( arg && arg.length && type !== "string" ) {
                            // Inspect recursively
                            add( arg );
                        }
                    });
                })( arguments );
                // Do we need to add the callbacks to the
                // 如果在执行回调的时候添加回调,则修正回调队列的长度

                if ( firing ) {
                    firingLength = list.length;
                // With memory, if we're not firing then
                // we should call right away
                // 如果参数设定中含有memory 并在回调队列中含有未执行的回调函数 则直接执行回调函数
                } else if ( memory ) {
                    firingStart = start;
                    fire( memory );
                }
            }
            return this;
        },
        // Remove a callback from the list
        // 删除回调函数
        remove: function() {
            if ( list ) {
                jQuery.each( arguments, function( _, arg ) {
                    var index;
                    while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                        list.splice( index, 1 );
                        // Handle firing indexes

                        if ( firing ) {
                            if ( index <= firingLength ) {
                                firingLength--;
                            }
                            if ( index <= firingIndex ) {
                                firingIndex--;
                            }
                        }
                    }
                });
            }
            return this;
        },
        // Check if a given callback is in the list.
        // If no argument is given, return whether or not list has callbacks attached.
        //----------------------------------判断函数是否已存在-------------
        has: function( fn ) {
            return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
        },
        // Remove all callbacks from the list
        // -------------------清空所有----------
        empty: function() {
            list = [];
            firingLength = 0;
            return this;
        },
        // Have the list do nothing anymore
        // 
        disable: function() {
            list = stack = memory = undefined;
            return this;
        },
        // Is it disabled?
        disabled: function() {
            return !list;
        },
        // Lock the list in its current state
        // 将堆禁止
        // 与disable的区别
        lock: function() {
            stack = undefined;
            if ( !memory ) {
                self.disable();
            }
            return this;
        },
        // Is it locked?
        locked: function() {
            return !stack;
        },
        // Call all callbacks with the given context and arguments
        fireWith: function( context, args ) {
            if ( list && ( !fired || stack ) ) {
                args = args || [];
                args = [ context, args.slice ? args.slice() : args ];
                //当回调函数执行时执行其他回调时,将执行回调的上下文以及参数信息添加到stack中
                if ( firing ) {
                    stack.push( args );
                } else {
                    fire( args );
                }
            }
            return this;
        },
        // Call all the callbacks with the given arguments
        // 当添加的函数执行时需要传入参数时,将参数通过fire传入
        fire: function() {
            self.fireWith( this, arguments );
            return this;
        },
        // To know if the callbacks have already been called at least once
        fired: function() {
            return !!fired;
        }
    };

return self;
};

总结:callback中心思想就是一个观察者模式。通过add方法将回调函数添加到一个数组中,通过fire方法执行回调函数。它有四个配置参数分别为unique、once、memory和stopOnFalse。unique 主要是回调函数是唯一的,once方法是值fire方法只能执行一次,通过执行一次后将数组变为undefined实现,memory通过添加后立即执行实现,stopOnFalse通过将memory设置为false并跳出执行程序实现

 类似资料: