zoukankan      html  css  js  c++  java
  • jquery1.8.3 callbacks源码分析

    先附上源码如下

    var optionsCache = {};//缓存options
    
    function createOptions( options ) {  //接受字符串参数,如 传入"unique memory" 
        var object = optionsCache[ options ] = {}; 
        jQuery.each( options.split( core_rspace ), function( _, flag ) {  //按空格分离字符串
            object[ flag ] = true; //optionsCache["unique memory"]["unique"]=true, optionsCache["unique memory"]["memory"]=true
        });
        return object; 
    }
    
    //不管是否传入参数都返回内部的self对象,可以调用对象的add,fire等方法
    jQuery.Callbacks = function( options ) {
        //判断传入参数是否为字符串,是则去optionsCache去获取,没有获取到的话则createOptions创建并返回对象,非字符串,用extend返回对象
        options = typeof options === "string" ?
            ( optionsCache[ options ] || createOptions( options ) ) :
            jQuery.extend( {}, options );
    
        var memory,
            fired,
            firing,
            firingStart,
            firingLength,
            firingIndex,
            list = [],
            stack = !options.once && [],
            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; 
                        break;
                    }
                }
                firing = false;
                if ( list ) {
                    if ( stack ) {
                        if ( stack.length ) {
                            fire( stack.shift() );
                        }
                    } else if ( memory ) {
                        list = [];
                    } else {
                        self.disable();
                    }
                }
            },
            self = {
                add: function() {
                    if ( list ) {//判断list是否存在
                        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 ) ) {//如果是unique唯一的,判断list数组是否存在,存在则不push
                                        list.push( arg );//为list添加函数
                                    }
                                } else if ( arg && arg.length && type !== "string" ) {//类数组或数组递归调用,可以看出add接受的为函数列表
                                    add( arg );
                                }
                            });
                        })( arguments );
    
                        if ( firing ) {
                            firingLength = list.length;
                        } else if ( memory ) {
                            firingStart = start;
                            fire( memory );
                        }
                    }
                    return this;
                },
                remove: function() {
                    if ( list ) {
                        jQuery.each( arguments, function( _, arg ) {
                            var index;
                            while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                                list.splice( index, 1 );
                                if ( firing ) {
                                    if ( index <= firingLength ) {
                                        firingLength--;
                                    }
                                    if ( index <= firingIndex ) {
                                        firingIndex--;
                                    }
                                }
                            }
                        });
                    }
                    return this;
                },
                has: function( fn ) {//判断fn是否存在list中
                    return jQuery.inArray( fn, list ) > -1;
                },
                empty: function() {
                    list = [];
                    return this;
                },
                disable: function() {
                    list = stack = memory = undefined;
                    return this;
                },
                disabled: function() {
                    return !list;
                },
                lock: function() {
                    stack = undefined;
                    if ( !memory ) {
                        self.disable();
                    }
                    return this;
                },
                locked: function() {
                    return !stack;
                },
                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;
                },
                fire: function() {//触发callbacks
                    self.fireWith( this, arguments );
                    return this;
                },
                fired: function() {
                    return !!fired;
                }
            };
    
        return self;
    };

    上面没有全写注释是因为感觉说不清楚,下面按支持的flags分几种情况

    1、直接用的情况

    function f1(v){

    console.log("f1:"+v);

    }

    function f2(v){

    console.log("f2:"+v);

    }

    function f3(v){

    console.log("f3:"+v);

    }

    var cb=$.Callbacks();

    cb.add(f1);

    cb.fire("foo");//f1:foo

    cb.add(f2,f3);

    cb.fire("bar");//f1:bar f2:bar  f3:bar 

    cb.fire("bar2");//f1:bar2 f2:bar2  f3:bar2 

    可以看出cb可以add在add,fire在fire

    跟进源码分析

    cb.add中把函数push进list数组,firing,memory都为false不用管,add操作ok

    cb.fire调用了fireWith,可以看出fireWith可以自己绑定context,fire则默认为this

    fireWith: function( context, args ) {
    args = args || [];
    args = [ context, args.slice ? args.slice() : args ];
    if ( list && ( !fired || stack ) ) { //!fired和stack均为true
      if ( firing ) { //firing为false
        stack.push( args );
      } else {
        fire( args );
      }
    }
    return this;
    }

    可以看出调用了Callbacks内部的fire函数,里面循环调用list里的函数,firingIndex一直为0,所以再次add或调用,里面的list函数都重新执行一遍,接着

    if ( list ) {
    if ( stack ) {//这里为true
    if ( stack.length ) {//为false,所以什么都不做,cb.fire操作也ok结束
    fire( stack.shift() );
    }
    } else if ( memory ) {
    list = [];
    } else {
    self.disable();
    }
    }

    2传入once的情况

    var cb=$.Callbacks(“once”);

    cb.add(f1);

    cb.fire("foo");//f1:foo

    cb.add(f2,f3);

    cb.fire("bar");//无

    cb.fire("bar2");//无

    cb.add同上

    cb.fire基本同上,只不过这里不同

    if ( list ) {
    if ( stack ) {//为false
    if ( stack.length ) {
    fire( stack.shift() );
    }
    } else if ( memory ) {
    list = [];
    } else {
    self.disable();//走到了这里 list = stack = memory = undefined;都设置了undefined,当你在add或fire的时候,list为undefined,所以不做任何操作
    }
    }

    3传入memory的情况

    var cb=$.Callbacks(“memory”);

    cb.add(f1);

    cb.fire("foo");//f1:foo

    cb.add(f2,f3);//f2:foo f3:foo

    cb.fire("bar")//f1:bar f2:bar f3:bar

    这里与第一种情况基本相同,

    if ( firing ) {
    firingLength = list.length;
    } else if ( memory ) {//只不过在第一次以后的add的时候,走这里
    firingStart = start;//这里改变长度,为只给新添加的函数执行
    fire( memory );//在add的时候把上次fire的数据传给此次add的函数执行
    }

    3传入unique的情况

    这个比较简单,在add的时候判断添加的函数是否已经存在

    if ( !options.unique || !self.has( arg ) ) {//如果是unique唯一的,判断list数组是否存在,存在则不push
    list.push( arg );//为list添加函数
    }

    4传入stopOnFalse的情况

    if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//可以看到就这里有stopOnFalse 这种情况就break了
    memory = false; 
    break;
    }

    5传入多个的情况

    1)"unique memory"这种情况就是添加的重复的不执行

    2)"memory stopOnFalse "

    function f1(v){

    console.log("f1:"+v);
    return false

    }

    function f2(v){

    console.log("f2:"+v);

    }

    function f3(v){

    console.log("f3:"+v);

    }

    var cb=$.Callbacks("memory stopOnFalse");

    cb.add(f1);

    cb.fire("foo");//f1:foo

    cb.add(f2,f3);//无

    if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//可以看到就这里有stopOnFalse 这种情况就break了
    memory = false; 可以看到这里memory =false
    break;
    }

    所以add的时候

    if ( firing ) {
    firingLength = list.length;
    } else if ( memory ) {//不走这里
    firingStart = start;
    fire( memory );//不执行
    }

    3)"memory once"

    var cb=$.Callbacks("memory once");

    cb.add(f1);

    cb.fire("foo");//f1:foo

    cb.add(f2,f3);//f2:foo f3:foo

    cb.fire("bar")//无

    在第一次fire时,

    if ( list ) {
    if ( stack ) {
    if ( stack.length ) {
    fire( stack.shift() );
    }
    } else if ( memory ) {//走了这里,list重置
    list = [];
    } else {
    self.disable();
    }
    }
    在下一次add时,
    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 );
    }
    当你在fire时,list为[]所以什么都不执行,再次充值list=[]

    6执行的时候在add或fire的情况

    function f1(v){

    console.log("f1:"+v);
    cb.add(f3)

    }

    function f2(v){

    console.log("f2:"+v);

    }

    function f3(v){

    console.log("f3:"+v);

    }

    var cb=$.Callbacks("memory");

    cb.add(f1);

    cb.fire("foo");//f1:foo f3:foo

    cb.add(f2);//f2:foo

    你可以发现当f1执行时又cb.add了

    if ( firing ) {//这时firing为true
    firingLength = list.length;//加长到当前list数组长度,以便执行新添加的
    } else if ( memory ) {
    firingStart = start;
    fire( memory );
    }

    至于其他组合和情况以及empty、remove、lock方法我想你能分析了...good job...

  • 相关阅读:
    利用jQuery Ajax技术实现每隔5秒向某页面传值
    POJ 题目2761 Feed the dogs(主席树||划分树)
    【翻译自mos文章】将expdp的dmp文件从asm磁盘组里边放到本地文件系统里边
    【算法拾遗】最大数和最小数
    WinMain和MFC的差别
    3.1 The Interpolating Polynomial(站点)
    在VS中设置比较和谐的字体和颜色的方法
    获取微信服务器IP地址
    关于bcg库记忆界面的问题及其解决办法
    Objective-C基础笔记(8)Foundation常用类NSString
  • 原文地址:https://www.cnblogs.com/ygm125/p/2800788.html
Copyright © 2011-2022 走看看