jQuery源码笔记.jpg

jQuery源码 Callbacks

2020-04-15  本文已影响0人  柠檬果然酸

官方demo

function fn1(value) {
    console.log(value);
}

function fn2(value) {
    fn1("fn2 says: " + value);
    return false;
}

可以将上述两个方法作为回调函数,并添加到 $.Callbacks 列表中,并按下面的顺序调用它们:

var callbacks = $.Callbacks();
callbacks.add(fn1);
// outputs: foo!
callbacks.fire("foo!");
callbacks.add(fn2);
// outputs: bar!, fn2 says: bar!
callbacks.fire("bar!")

这样做的结果是,当构造复杂的回调函数列表时,将会变更很简单。可以根据需要,很方面的就可以向这些回调函数中传入所需的参数。

上面的例子中,我们使用了 $.Callbacks() 的两个方法: .add() 和 .fire()。 .add() 和 .fire() .add() 支持添加新的回调列表, 而.fire() 提供了一种用于处理在同一列表中的回调方法的途径。

另一种方法是$.Callbacks 的.remove()方法,用于从回调列表中删除一个特定的回调。下面是.remove()使用的一个例子

var callbacks = $.Callbacks();
callbacks.add( fn1 );
// outputs: foo!
callbacks.fire( "foo!" );
callbacks.add( fn2 );
// outputs: bar!, fn2 says: bar!
callbacks.fire( "bar!" );
callbacks.remove( fn2 );
// only outputs foobar, as fn2 has been removed.
callbacks.fire( "foobar" );

jQuery.Callbacks还提供“once memory”等参数用来处理
1.once: 确保这个回调列表只执行( .fire() )一次(像一个递延 Deferred)。
2.memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred)。
3.unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调)。
4.stopOnFalse: 当一个回调返回false 时中断调用。

var callbacks = $.Callbacks('once');

callbacks.add(function() {
    alert('a');
})

callbacks.add(function() {
    alert('b');
})

callbacks.fire(); //输出结果: 'a' 'b'
callbacks.fire(); //未执行

jQuery回调模块结构
jQuery.Callbacks()的API列表如下

callbacks.add()        :回调列表中添加一个回调或回调的集合。
callbacks.disable()    :禁用回调列表中的回调。
callbacks.disabled()   :确定回调列表是否已被禁用。 
callbacks.empty()      :从列表中删除所有的回调。
callbacks.fire()       :用给定的参数调用所有的回调。
callbacks.fired()      :访问给定的上下文和参数列表中的所有回调。 
callbacks.fireWith()   :访问给定的上下文和参数列表中的所有回调。
callbacks.has()        :确定列表中是否提供一个回调。
callbacks.lock()       :锁定当前状态的回调列表。
callbacks.locked()     :确定回调列表是否已被锁定。
callbacks.remove()     :从回调列表中的删除一个回调或回调集合。

源码结构

jQuery.Callbacks = function(options) {
    options = typeof options === "string" ?
        (optionsCache[options] || createOptions(options)) :
        jQuery.extend({}, options);
    //实现代码
    fire = function() {}
    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() {}
    };
    return self;
};
// 非空格
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );

// Convert String-formatted options into Object-formatted ones
function createOptions( options ) {
    var object = {};
    jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
        object[ flag ] = true;
    } );
    return object;
}

源码

// 回调模块
jQuery.extend( {
    
    // once: 确保这个回调列表只执行( .fire() )一次(像一个递延 Deferred)
    // memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred)
    // unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调)
    // stopOnFalse: 当一个回调返回false 时中断调用
    Callbacks: function( options ) {
        options = typeof options === 'string' ?
            createOptions( options ) :
            jQuery.extend( {}, options )
            
        var
            firing, // list是否使用中
            memory, // { Callbacks对象, fire()的参数 }
            fired, // list是否使用过
            locked,
            list = [], // 观察者集合
            queue = [], // 保存着上一次调用fire()时传入的参数
            firingIndex = -1, // 当传入参数有memory时要用到这个参数定位
            
            fire = function() {
                locked = locked || options.once;
                
                // list使用中
                fired = firing = true;
                
                // 这个外层循环是个摆设,只循环1次
                // 最重要的是执行完循环后firingIndex = -1
                for ( ; queue.length; firingIndex = -1 ) {
                    memory = queue.shift();
                    
                    // 循环执行list中的函数
                    // 这代码的可读性真让人头皮发麻
                    // 有时候为了追求极简写法会让人摸不着头脑
                    while ( ++firingIndex < list.length ) {

                        // 执行函数并且检查是否需要提前终止
                        if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
                            options.stopOnFalse ) {
                            
                            firingIndex = list.length;
                            memory = false;
                        }
                    }
                }
                
                // 如果options中没有memory,清空memory
                // memory要用于add方法的判断,如果里面有值就会触发add中fire方法的调用
                if ( !options.memory ) {
                    memory = false;
                }
                
                // list使用结束
                firing = false;
                
                // Clean up if we're done firing for good
                if ( locked ) {

                    // Keep an empty list if we have data for future add calls
                    if ( memory ) {
                        list = [];

                    // Otherwise, this object is spent
                    } else {
                        list = "";
                    }
                }
            },
            
            self = {
                
                // Add a callback or a collection of callbacks to the list
                add: function() {
                    if ( list )  {
                        if ( memory && !firing ) {
                            firingIndex = list.length - 1;
                            queue.push( memory );
                        }
                        
                        // 这是一个立即执行的函数,而且声明的add方法也和自身没有任何关系,就是名字相同而已,不存在函数重写
                        ( function add( args ) {
                            // 传入的可能不止一个函数,多个函数就需要each一下
                            jQuery.each( args, function( _, arg ) {
                                if ( isFunction( arg ) ) {
                                    // 两种情况
                                    // 1.如果options中没有unique
                                    // 2.如果options中有unique,但list中没有该函数
                                    if ( !options.unique || !self.has( arg ) ) {
                                        list.push( arg );
                                    }
                                } else if ( arg && arg.length && toType( arg ) !== "string" ) { // arg有值 && arg长度不为0 && arg不为字符串(arg可能是个函数数组)

                                    // 递归下去
                                    add( arg );
                                }
                            } );
                        } )( arguments );
                        
                        // 如果options中有memory,add过程中会立即执行当前函数,并且传入当前函数的参数是上一轮调用fire留下来的参数
                        if ( memory && !firing ) {
                            fire();
                        }
                    }
                    return this;
                },
                
                // Remove a callback from the list
                remove: function() {
                    // 老规矩,要移除的方法不止一个
                    jQuery.each( arguments, function( _, arg ) {
                        var index;
                        while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                            // splice()方法从数组中删除项目
                            list.splice( index, 1 );

                            // Handle firing indexes
                            // firingIndex的值基本上为-1
                            // 如果出现index <= firingIndex,说明此时fire()方法正在运行
                            if ( index <= firingIndex ) {
                                firingIndex--;
                            }
                        }
                    } );
                    return this;
                },
                
                // 查找list中是否有传入的函数
                has: function( fn ) {
                    return fn ?
                        jQuery.inArray( fn, list ) > -1 :
                        list.length > 0;
                },
                
                // Remove all callbacks from the list
                empty: function() {
                    if ( list ) {
                        list = [];
                    }
                    return this;
                },
                
                // Disable .fire and .add
                // Abort any current/pending executions
                // Clear all callbacks and values
                disable: function() {
                    locked = queue = [];
                    list = memory = "";
                    return this;
                },
                disabled: function() {
                    return !list;
                },

                // Disable .fire
                // Also disable .add unless we have memory (since it would have no effect)
                // Abort any pending executions
                // 上锁:其表现结果就是不能调用fire()方法,但是能调用add
                // 如果option是memory依然会在add()之后调用fire()方法
                lock: function() {
                    // 设置locked和queue指向同一个地址
                    locked = queue = [];
                    if ( !memory && !firing ) {
                        list = memory = "";
                    }
                    return this;
                },
                locked: function() {
                    return !!locked;
                },
                
                // Call all callbacks with the given context and arguments
                fireWith: function( context, args ) {
                    if ( !locked ) {
                        args = args || [];
                        args = [ context, args.slice ? args.slice() : args ];
                        // 把[ context, 参数 ]放进queue里面
                        queue.push( args );
                        if ( !firing ) {
                            fire();
                        }
                    }
                    return this;
                },
                
                // Call all the callbacks with the given arguments
                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;
    }
} )
上一篇下一篇

猜你喜欢

热点阅读