首页 > 代码库 > jquery中的callbacks之我见

jquery中的callbacks之我见

callbacks是jquery的核心之一。

语法如下:

jQuery.Callbacks( flags )   flags 类型: String 一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为。

once: 确保这个回调列表只执行( .fire() )一次(像一个递延 Deferred).

memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred).

unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调).

stopOnFalse: 当一个回调返回false 时中断调用

使用案例:

function fn1(val) {        console.log(‘fn1 says ‘ + val);    }    function fn2(val) {        console.log(‘fn2 says ‘ + val);        return false;    }    var cbs = $.Callbacks(‘once memory‘);    cbs.add(fn1);    //第一次fire会缓存传入的参数    cbs.fire(‘foo‘); //fn1 says foo    //fire过一次之后,以后的add都会自动调用fire,传入的参数是上次fire传入的‘foo‘    cbs.add(fn2); //fn2 says foo
 function fn1(val) {        console.log(‘fn1 says ‘ + val);    }    function fn2(val) {        console.log(‘fn2 says ‘ + val);        return false;    }    var cbs = $.Callbacks(‘stopOnFalse‘);    cbs.add(fn2)    cbs.add(fn1);    cbs.fire(‘foo‘); //fn2 says foo  
function fn1(val) {        console.log(‘fn1 says ‘ + val);    }    function fn2(val) {        console.log(‘fn2 says ‘ + val);        return false;    }    var cbs = $.Callbacks(‘once‘);    cbs.add(fn2)    cbs.add(fn1);    cbs.fire(‘foo‘); //fn2 says foo    fn1 says foo      cbs.add(fn2);    cbs.fire("bar"); //不输出

源码分析:

var optionsCache = {};    // Convert String-formatted options into Object-formatted ones and store in cache    //20170618 huanhua 参数options="once memory"    // 返回结果: optionsCache[once memory]={once:true,memory:true}function createOptions( options ) {    var object = optionsCache[options] = {};    //20170618 huanhua 前面已经定义正则 core_rnotwhite= /\S+/g , options.match( core_rnotwhite )返回数组["once","memory"]类似这种    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 // Flag to know if list is currently firing        firing,        // Last fire value (for non-forgettable lists)        //20170618 huanhua 存储的是最后一次 fire传递的参数值        memory,        // Flag to know if list was already fired        fired,        // End of the loop when firing        firingLength,        // Index of currently firing callback (modified by remove if needed)        firingIndex,        // First callback to fire (used internally by add and fireWith)        firingStart,        // Actual callback list        // 20170618 huanhua 回调函数的存储列表        list = [],        // Stack of fire calls for repeatable lists        // 20170618 huanhua 回调函数不是只执行一次的时候,用 stack保存正在执行回调的时候,再次请求执行的 fire()中传入的参数        stack = !options.once && [],        // Fire callbacks        fire = function (data) {//20170618 data是一个数组[context,arg],第一个是执行的上下午,第二个是真正的参数            memory = options.memory && data;            fired = true;            firingIndex = firingStart || 0;            firingStart = 0;            firingLength = list.length;            firing = true;            for (; list && firingIndex < firingLength; firingIndex++) {                //20170618 huanhua 判断                if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {                    // 阻止未来可能由于add所产生的回调,add中有这段代码                    //else if ( memory ) {                   // firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行                   // fire( memory ); //20170618 huanhua 立即执行回调函数                  //}                    memory = false; // To prevent further calls using add                    break;//由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环                }            }            firing = false;            if (list) {                //20170618 huanhua 处理正在执行回调中,再次触发的 fire(),也就是同时第一个触发的 fire还没执行完,紧接着多次执行了 fire                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) {                        //20170618 huanhua add(fun1,fun2,fun3)                        jQuery.each( args, function( _, arg ) {                            var type = jQuery.type(arg);                            //20170618 huanhua 判断是函数                            if (type === "function") {                                //20170618 huanhua  !options.unique判断 当$.Callbacks(‘unique‘)时,保证列表里面不会出现重复的回调                                //!self.has( arg )只要不列表里不存在,就添加到列表                                if ( !options.unique || !self.has( arg ) ) {                                    list.push( arg );                                }                                //20170618 huanhua 判断传递的参数是类似数组的对象,或者就是数组                                // add({leng:2,0:fun1,1:fun2},{leng:1,0:fun3});                            } else if ( arg && arg.length && type !== "string" ) {                                // Inspect recursively                                add( arg );                            }                        });                    })( arguments );                    // Do we need to add the callbacks to the                    // current firing batch?                    //20170618 huanhua 当fire执行的时间需要比较长的时候,我们在执行 add的时候,fire也在执行,add执行完后,fire还没执行完,为了防止新加的不执行,                    //所以重新赋值了需要执行的回调函数个数 firingLength = list.length;                    if ( firing ) {                        firingLength = list.length;                    // With memory, if we‘re not firing then                    // we should call right away                    //20170618 huanhuaa $.CallBacks("memory"),并且还调用了 fire                     } else if ( memory ) {                        firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行                        fire( memory ); //20170618 huanhua 立即执行回调函数                    }                }                return this;            },            // Remove a callback from the list            //20170618 huanhua 删除指定的回调函数            remove: function () {                //20170618  huanhua 判断回调函数列表是有效的                if ( list ) {                    jQuery.each( arguments, function( _, arg ) {                        var index;                        //20170618 huanhua index = jQuery.inArray( arg, list, index )获取需要被删除的回调函数在列表中的位置                        while ((index = jQuery.inArray(arg, list, index)) > -1) {                            //20170618 huanhua 删除回调函数                            list.splice( index, 1 );                            // Handle firing indexes                            //20170618 huanhua 如果删除的时候在执行 fire()                            if (firing) {                                //20170618 huanhua 当被删除的回调函数的位置 <= 回调函数列表总长度的时候,删除了一个回调函数,所以 firingLength 要减一个了                                if ( index <= firingLength ) {                                    firingLength--;                                }                                //20170618 huanhua 当被删除的回调函数的位置 <= 执行到的回调函数的位置时候,删除了一个回调函数,所以 firingIndex 要减一个了                                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) {//回调函数是否在列表中.                //20170616 huanhua !!( list && list.length ) 如果执行的 has("")/has(undefined)等等,如果 执行了  def.disable(),list就等于undefined了                return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );            },            // Remove all callbacks from the list            //20170618 huanhua 清空回调函数列表            empty: function() {                list = [];                return this;            },            // Have the list do nothing anymore            //20170618 huanhua 禁用回调列表中的回调。            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 ) {                args = args || [];                args = [ context, args.slice ? args.slice() : args ];                if (list && (!fired || stack)) {                    //20170618 huanhua 如果正在执行回调函数                    //将参数推入堆栈,等待当前回调结束再调用                    if ( firing ) {                        stack.push(args);                    //20170618 huanhua 如果当前不是正在执行回调函数,就直接执行                    } 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;};

 

jquery中的callbacks之我见