jquery的2.0.3版本源碼系列(6):2880-3042行,回調對象,對函數的統一管理

目錄jquery

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循環來作到。緩存

回調對象的好處是統一管理。app

看一個未使用回調對象的例子:ide

<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個選項。this

once的做用是隻能觸發一次。spa

<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的做用是記憶功能。code

<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;
            }
相關文章
相關標籤/搜索