jquery的2.0.3版本源码系列(6)-2880-3042行,回调对象,对函数的统一管理

来源:互联网 发布:电纸书阅读器软件 编辑:程序博客网 时间:2024/04/20 11:10

目录

1 . 回调对象callbacks的演示

回调的使用有一点像事件绑定,先绑定好,等到有点击事件或者其他时就触发。

 <script src="js/jquery-2.0.3.js"></script>    <script>        function aaa(){            alert(1);        }        function bbb(){            alert(2);        }        function ccc(){            alert(3);        }        var cb=$.Callbacks();        cb.add(aaa);        cb.add(bbb);        cb.add(ccc);        cb.fire();        //看起来有点像事件绑定,add负责添加,fire负责触发    </script>

工作原理相当于add方法负责收集事件list,fire负责统一触发,触发时以for循环来做到。

回调对象的好处是统一管理。

看一个未使用回调对象的例子:

<script src="js/jquery-2.0.3.js"></script>    <script>       function aaa(){           alert(1);       }       (function(){           function bbb(){               alert(2);           }       })();       aaa();//可调用        bbb();//报错,说not defined    </script>

那么为了能够调用匿名函数里的bbb函数,可以绑定到全局的回调对象上。

<script src="js/jquery-2.0.3.js"></script><script>        var  cb=$.Callbacks();        function aaa(){            alert(1);        }        cb.add(aaa);        (function(){            function bbb(){                alert(2);            };            cb.add(bbb);        })();       cb.fire();//依次弹出1和2
</script>

2.callbacks的参数说明                                                                  

1.4个选项

回调函数有once、memory、unique、stopOnFalse个选项。

once的作用是只能触发一次。

<script src="js/jquery-2.0.3.js"></script>    <script>        function aaa(){            alert(1);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks('once');        cb.add(aaa);        cb.add(bbb);        cb.fire();//生效        cb.fire();//并不生效    </script>

memory的作用是记忆功能。

<script src="js/jquery-2.0.3.js"></script>    <script>        function aaa(){            alert(1);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks('memory');        cb.add(aaa);        cb.fire();//两个函数都生效        cb.add(bbb);    </script>

unique的作用是对相同的函数去重。

<script src="js/jquery-2.0.3.js"></script>    <script>        function aaa(){            alert(1);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks('unique');        cb.add(aaa);        cb.add(aaa);        cb.fire()//这样就只触发一次aaa    </script>

stopOnFalse的作用是当函数返回值为false时就不再执行后续函数。

<script src="js/jquery-2.0.3.js"></script>    <script>        function aaa(){            alert(1);            return false;        }        function  bbb(){            alert(2);        }        var cb=$.Callbacks('stopOnFalse');        cb.add(aaa);        cb.add(bbb);        cb.fire()//遇到返回False,那么后续就不再执行    </script>

另:callbacks接收多个选项的组合,比如 var cb=$.Callbacks('once unique'); 。

2.options源码

有3个部分

var optionsCache = {};

这里定义了选项缓存的空对象。

function createOptions( options ) {    var object = optionsCache[ options ] = {};        //定义了一个options作为属性的对象    jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {        object[ flag ] = true;    });      //options.match( core_rnotwhite )根据空格分割字符串,并返回结果数组    return object;}

那么传进来的选项options进行字符串分割, var cb=$.Callbacks('once unique'); 的option匹配的结果是 ['one','unique'] 。接下来在each循环里第一个选项时索引,第二个选项分别是one和unique。

所以把选项存进optionsCache并返回object。

<script>        var optionsCache={};        function  cache (options){            var object = optionsCache[ options ] = {};            object[options]=true;            console.log(object);//{once:true}        }        cache('once');    </script>
创建选项object
options = typeof options === "string" ?        ( optionsCache[ options ] || createOptions( options ) ) :        jQuery.extend( {}, options );

这段代码判断了options是不是字符串,如果不是,比如 $.Callbacks() 那么返回的就是空对象。如果是字符串,创建固定格式的选项,能从缓存里面取就直接取,不能就构造出来。类似如下:

optionsCache:{            'once memory':{once:true,memory:true}        }
options:{once:true,memory:true}

3.1  定义了一些变量                                     

    1.memory

    2.fired

    3.firing

    4.firingStart

    5.firingLength

    6.firingIndex

    7.list是实际的回调列表

    这是回调对象的最重要的一个数组了,通过把回调函数push到list中,以此来决定触发的列表。

   8.stack

     stack = !options.once && [] 通过代码可以看到如果选项的once为true,那么结果为false,过选项的once为false也就是不要设置,那么stack就为true了。

   9.fire方法

    它是一个辅助方法,供self对象的fire、fireWith等调用。

   10.self对象

   定义了对外的方法。一旦使用jQuery.callbacks就会返回self对象,那么其间定义的方法就可以被调用了。

3.2  add方法                                             

    add方法放在self对象里。

1.分析无参的情况。为了测试这个方法做了什么。这里介绍一个执行源代码的小技巧。先把代码设置为

<script src="js/jquery-2.0.3.js"></script>    <script>        function aaa() {            alert(1);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks();    </script>
还未使用add方法
var start = list.length;console.log(arguments);
源代码添加控制台log方法

这里的打印是代码初始化的过程。

准备工作好了。

脚本代码如下:

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

这个时候的console.log打印,那么内置arguments对象就分别是aaa和bbb了。

为什么会each遍历呢,针对的是 cb.add(aaa,bbb); ,那么argument对象就有了2个元素了。

self = {            // 添加一个回调或者一个回调列表            add: function() {                if ( list ) {                    //第一次进入的时候当然list空数组,start等于0,第二次等于1.                    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" ) {                                // 这里处理的是add([aaa,bbb])数组类型的参数                                add( arg );                            }                        });                    })( arguments );                                     .........                }                return this;            },

通过一个自执行add匿名函数,对arguments进行遍历。

如果为function进入if分支。这个时候有一个unique判断。当然无参的时候unique是undefined咯,所以会进入list.push方法。那么也就是说所有添加的函数都会被push到list中。

如果出现数组参数,比如 cb.add([aaa,bbb]); ,那么就把这个数组再递归调用一遍,当然是遍历数组咯。

 2.处理unique参数

if ( !options.unique || !self.has( arg ) ) {                                    list.push( arg );                                }

我们看到unique一开始为true,那么 !options.unique 第一次进入时为false,就要看self是否有arg了。第一次添加某个函数,false取反就为true。第二次添加相同的函数,true取反就为false了。所以第二次添加相同的函数时,是不可能push到list中的。那么也就实现了unique的唯一添加目标了。

3.处理memory参数

我们知道memory参数的作用是如果回调已经被触发了,那么再次添加add方法,会自动触发。

 <script src="js/jquery-2.0.3.js"></script>    <script>        function aaa() {            alert(1);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks("memory");        cb.add(aaa);        cb.fire();        cb.add(bbb);//弹出2    </script>

那么在add方法里,memory是什么呢,搜索一下,2909行memory等于之前触发的data memory = options.memory && data; 。

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 );                    }

比如,

<script src="js/jquery-2.0.3.js"></script>    <script>        function aaa(n) {            alert(n);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks("memory");        cb.add(aaa);        cb.fire(999);//fire函数传参        cb.add(bbb);//弹出2    </script>

这个时候memory就被赋值为如下打印内容,自然是包含传进去的参数999的。同时因为add方法里的start赋值,现在已经变为了1,通过firingStart的矫正,那么就只触发list最后一个函数了。

 

3.3   remove方法

// 从触发list里移除某个回调            remove: function() {                if ( list ) {                    jQuery.each( arguments, function( _, arg ) {                        var index;//  先遍历触发列表,然后通过splice方法裁减掉                        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;            },

首先上脚本代码。一旦函数aaa被remove掉,就不会被触发。

 <script src="js/jquery-2.0.3.js"></script>    <script>        function aaa() {            alert(1);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks();        cb.add(aaa,bbb);        cb.remove(bbb);        cb.fire();//bbb并不会被触发    </script>

其原理是从list中删除掉。

根据源码,先找出arguments,其打印结果为

那么接下来就看源码执行了。

通过each方法可以把要remove掉的函数取出来。 ( index = jQuery.inArray( arg, list, index ) ) > -1 是把remove掉的函数在list中取到索引,然后通过slice删除对应的元素。就做到了从list中删除某个函数的触发了。

最后看firing跟onStopFalse参数有关。参看fire方法。

3.4  has方法

 <script>        function aaa() {            alert(1);        }        function bbb(){            alert(2);        }        var cb=$.Callbacks();        cb.add(aaa);        cb.has(aaa);//true    </script>
has: function( fn ) {                return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );            },//inArray方法传两个参数,作用是fn在数组list里的索引。大于-1当然是存在咯。//fn不存在比如cb.has(),就返回list和list的长度的逻辑值,自然是true。//list本来就不存在,那么是false

如果传进去一个函数,那么进行条件判断,返回true或者FALSE。

3.5  empty方法

//从list中删除所有的回调            empty: function() {                list = [];                firingLength = 0;                return this;            },

要清空所有的回调要做两件事情,首先list变为空数组,然后firingLength置为0。

3.6  disable、diabled、lock方法

  // 禁用disable: function() {     list = stack = memory = undefined;     return this;},// 是否被禁用了disabled: function() {     return !list;},// 在当前的状态锁住list    lock: function() {    stack = undefined;      if ( !memory ) {    self.disable();      }     return this;},// 它是否locked    locked: function() {    return !stack;},

3.7  fire一系列的方法

 首先看fire方法。

第一,我们需要了解的是传进去的data是什么。在回调对象中,供3个地方调用, fire( memory ); 、 fire( args ); 和 fire( stack.shift() ); 。为了简便,通过第三处代码的分析。

第二,stack到底从2903行的 stack = !options.once && [], 发生了什么。这里的意思是如果once是false的话,就会把空数组放到stack中。搜索stack可以看到只是在3023行有一个push动作。

fire = function( data ) {            memory = options.memory && data;//通过选项的memory返回true或者false            fired = true;//把fired置为true            firingIndex = firingStart || 0;//触发的起始索引,要么是firingStart,要么是默认0            firingStart = 0;            firingLength = list.length;//触发的长度为list的长度            firing = true;//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();                }            }        },

接下来看实例方法。

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;            },

最后的fired的意思是是否被触发过了。

// To know if the callbacks have already been called at least once            fired: function() {                return !!fired;            }

 

原创粉丝点击