首页 > 代码库 > jquery源码 Callback

jquery源码 Callback

工具方法。对函数的统一管理。

jquery2.0.3版本$.Callback()部分的源码如下:

技术分享
// String to Object options format cachevar optionsCache = {};// Convert String-formatted options into Object-formatted ones and store in cachefunction createOptions( options ) {    var object = optionsCache[ options ] = {};    jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {        object[ flag ] = true;    });    return object;}/* * Create a callback list using the following parameters: * *    options: an optional list of space-separated options that will change how *            the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * *    once:            will ensure the callback list can only be fired once (like a Deferred) * *    memory:            will keep track of previous values and will call any callback added *                    after the list has been fired right away with the latest "memorized" *                    values (like a Deferred) * *    unique:            will ensure a callback can only be added once (no duplicate in the list) * *    stopOnFalse:    interrupt callings when a callback returns false * */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 );    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 ) {            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();                }            }        },        // 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;                    (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;            },            // 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            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 ];                    if ( firing ) {                        stack.push( args );                    } else {                        fire( args );                    }                }                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;};
View Code

一、$.Callback()的简单使用及应用场景

1、$.Callback()的用法。

观察者模式,添加完后统一触发。

       function aaa(){            alert(1);        }        function bbb(){            alert(2);        }        var cb= $.Callbacks();        cb.add(aaa);        cb.add(bbb);        cb.fire();    

2、好处,应用场景。

要统一的管理aaa和bbb。有时候如下,很难对不同作用域下的函数进行统一管理。

  function aaa(){            alert(1);        }        (function(){            function bbb(){                alert(2);            }        })();        aaa();        bbb();

只能弹出1,因为bbb是局部作用域中的。

$callback可以做到。如下,只要cb是全局的。

 var cb= $.Callbacks();        function aaa(){            alert(1);        }        cb.add(aaa);        (function(){            function bbb(){                alert(2);            }            cb.add(bbb);        })();        cb.fire();

对应复杂情况很有用。统一管理,通过fire统一触发。

二、原理图

Callback接收一个参数,可以有4个选项,once,memory,unique,stopOnFalse。

self单体有这些方法:add,remove,has,empty,disable,disabled,lock,locked, fireWith,fire,fired。

list=[]数组变量,用来收集回调函数。fire的时候对其循环调用。

add:push数组

fire:调用fireWith,fireWith允许传参,fire可传可不传。

fireWith:调用私有函数fire,在私有函数fire中for循环list。

remove:splice数组。

4个参数:

  • once针对fire()只循环一次
  • memory 针对add,作用到add上,add时判断有memory就去执行fire。
  • unique 针对add,添加的时候就可以去重
  • stopOnFalse 针对fire,在for循环时遇到false,立即跳出循环

技术分享

三、更多用法

1、callback4个参数的作用

不传参数,fire几次就触发几次。

技术分享
function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        var cb = $.Callbacks();        cb.add(aaa);        cb.add(bbb);        cb.fire(); //1 2        cb.fire();//1 2
View Code
  • once:fire只能触发一次
        function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        var cb = $.Callbacks(‘once‘);        cb.add(aaa);        cb.add(bbb);        cb.fire(); //1 2        cb.fire();

不传参数,在fire之后add的回调不能被fire。

技术分享
//不写参数,只弹出1,2不会弹出 function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        var cb = $.Callbacks();        cb.add(aaa);        cb.fire(); //1        cb.add(bbb);
View Code
  • memory记忆,在fire前面后面add的方法都能得到执行。
function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        var cb = $.Callbacks(‘memory‘);        cb.add(aaa);        cb.fire(); //1 2        cb.add(bbb);
  • unique:去重
技术分享
//不加参数,add2次aaa,就会触发2次aaafunction aaa() {            alert(1);        }        var cb = $.Callbacks();        cb.add(aaa);        cb.add(aaa);        cb.fire(); //1 1
View Code
function aaa() {            alert(1);        }        var cb = $.Callbacks(‘unique‘);        cb.add(aaa);        cb.add(aaa);        cb.fire(); //1 加了unique参数,同样的函数不能多次add
  • stopOnFalse:函数返回false跳出循环
function aaa() {            alert(1);            return false;        }        function bbb() {            alert(2);        }        var cb = $.Callbacks();        cb.add(aaa);        cb.add(bbb);        cb.fire(); //1 2 不传参,第一个函数返回false时后面的函数也能正常执行
function aaa() {            alert(1);            return false;        }        function bbb() {            alert(2);        }        var cb = $.Callbacks(‘stopOnFalse‘);        cb.add(aaa);        cb.add(bbb);        cb.fire(); //1        //传参stopOnFalse,第一个函数返回false时后面的函数不再执行

2、callback也可以接收组合的形式

 function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        //组合使用,只执行一次,并且弹出1 2        var cb = $.Callbacks(‘once memory‘);        cb.add(aaa);        cb.fire(); //1        cb.add(bbb);        cb.fire();

源码中:
传入了 once和memory后,

options={once:true,memory:true}optionCache={"once memory":{once:true,memory:true}}

6、fire()可以传参

参数作为回调的实参

function aaa(n) {            alert("aaa "+n);        }        function bbb(n) {            alert("bbb "+n);        }        var cb = $.Callbacks();        cb.add(aaa);        cb.add(bbb);        //fire传参        cb.fire("hello"); //弹出aaa hello 和bbb hello

四、源码

Callbacks就是一个工具函数,内部定义了一个self ,add和remove还有has等挂在self上。

技术分享

1、add源码

 主要是把回调函数Push到数组list中。

       add: function() {            if ( list ) { //list初始化为[],if判断会返回true                // First, we save the current length                var start = list.length;                (function add( args ) {                    jQuery.each( args, function( _, arg ) { ////处理cb.add(aaa,bbb)这种调用                        var type = jQuery.type( arg );//arg就是每一个函数                        if ( type === "function" ) {//arg是函数就push到list中,此时有个判断有没有unique                            if ( !options.unique || !self.has( arg ) ) {//有unique走后面,判断list中有没有这个函数,有就不添加了                                list.push( arg );                            }                        } else if ( arg && arg.length && type !== "string" ) { //处理cb.add([aaa,bbb])这种调用                            // Inspect recursively                            add( arg );//递归分解,最终还是push到list                        }                    });                })( 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;        },

 2、remove源码

// 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 );//主要就是splice删除操作                        // Handle firing indexes                        if ( firing ) {                            if ( index <= firingLength ) {                                firingLength--;                            }                            if ( index <= firingIndex ) {                                firingIndex--;                            }                        }                    }                });            }            return this;        },

3、fire源码

1、整体调用逻辑

self的fire调用self的fireWith,fireWith把参数传递到fire()函数。

// 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 ];                    if ( firing ) {                        stack.push( args );                    } else {                        fire( args );                    }                }                return this;            },            // Call all the callbacks with the given arguments            fire: function() {                self.fireWith( this, arguments );                return this;            },

fire()时主要是for循环

 // Fire callbacks        fire = function( data ) {            memory = options.memory && data;            fired = true;//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 ) {//每次函数调用同时处理stopOnFalse的情况                    memory = false; // To prevent further calls using add //stopOnFalse后有memory也不好使了                    break;                }            }            firing = false;//触发结束            if ( list ) {                if ( stack ) {                    if ( stack.length ) {                        fire( stack.shift() );                    }                } else if ( memory ) {                    list = [];                } else {                    self.disable();                }            }        },

2、firing特殊情况

线通过例子来看一下效果

        function aaa() {            alert(1);            cb.fire(); //在这里调用fire()会出现什么问题 死循环        }        function bbb() {            alert(2);        }        var cb = $.Callbacks();        cb.add(aaa);        cb.add(bbb);        cb.fire(); 

在执行函数的过程中再次调用fire()的执行顺序是怎样的?

var bBtn=true;//用bBtn避免死循环        function aaa() {            alert(1);            if(bBtn){                cb.fire();//注意这里fire调用后执行顺序是1 2 1 2,而不是1 1 2 2                bBtn=false;            }        }        function bbb() {            alert(2);        }        var cb = $.Callbacks();        cb.add(aaa);        cb.add(bbb);        cb.fire();

结论:把函数运行过程中触发的fire()放到了运行过程的队列当中。

怎么做到的,看源码:

在fireWith的时候判断for循环有没有执行完

 fireWith: function( context, args ) {            if ( list && ( !fired || stack ) ) {                args = args || [];                args = [ context, args.slice ? args.slice() : args ];                if ( firing ) {//firing在for循环没有走完时一直是true                    stack.push( args );//所以这句话意思就是函数执行时再去fire()调用就会push到stack数组中                } else {                    fire( args );                }            }            return this;        },

再去调用fire()的时候

  // Fire callbacks        fire = function( data ) {            memory = options.memory && data;            fired = true;//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 ) {//每次函数调用同时处理stopOnFalse的情况                    memory = false; // To prevent further calls using add //stopOnFalse后有memory也不好使了                    break;                }            }            firing = false;//触发结束            if ( list ) {                if ( stack ) {        //这就是出现在函数执行过程中再次fire()的时候,等循环执行完,再去按顺序执行                    if ( stack.length ) {                        fire( stack.shift() );                    }                } else if ( memory ) {//只执行一次的时候,有once,memory就清空list,此时fire()就相当于一个执行一个空数组                    list = [];                } else {                    self.disable();//disable阻止后续任何的fire()操作                }            }        },

 针对下面这段源码的一个例子:once和memory同时存在的时候,fire()无效因为list为[]了,但是add仍然有效。

else if ( memory ) {//只执行一次的时候,有once,memory就清空list,此时fire()就相当于一个执行一个空数组                    list = [];                } else {                    self.disable();//disable阻止后续任何的fire()操作                }

disable阻止后续任何的fire()操作。

 function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        //组合使用,只执行一次,并且弹出1 2 3        var cb = $.Callbacks(‘once memory‘);        cb.add(aaa);        cb.fire(); //1        cb.fire();//此时list为[]        cb.add(bbb);        cb.fire();        function ccc(){            alert(3);        }        cb.add(ccc);

4、其他源码

has(fn):判断list有没有fn

empty: 清空数组list=[]

disable:全部锁住,禁止了,如下

// Have the list do nothing anymore            disable: function() {                list = stack = memory = undefined;                return this;            },

disabled:判断是不是禁止了。return !list;

lock:只是把stack锁住

// Lock the list in its current state            lock: function() {                stack = undefined;                if ( !memory ) {                    self.disable();                }                return this;            },

locked:是否locked。 return !stack;

5、 lock和disable的区别

disable禁止所有操作

 function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        var cb = $.Callbacks(‘memory‘);        cb.add(aaa);        cb.fire(); //1        cb.disable();//disable()后只能弹出1 因为禁止所有操作了,虽然有Memory        cb.add(bbb);//不起作用了,此时list变为undefined        cb.fire();//不起作用了

lock只是锁住数组

function aaa() {            alert(1);        }        function bbb() {            alert(2);        }        var cb = $.Callbacks(‘memory‘);        cb.add(aaa);        cb.fire(); //1 2        cb.lock();//lock()只是把后续的fire()锁住,其他操作是锁不住的        cb.add(bbb);        cb.fire();//不起作用了 此时list为[]

 

本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/6885500.html有问题欢迎与我讨论,共同进步。

jquery源码 Callback