JQuery中的回調對象

JQuery中的回調對象

回調對象(Callbacks object)模塊是JQuery中的一個很基礎的模塊,不少其餘的模塊(好比Deferred、Ajax等)都依賴於Callbacks模塊的實現。
本文主要描述回調對象的基本功能、具體實現,以及使用到的設計思想。jquery

1. 基本功能

從jquery的API 中能夠了解到,Callbacks對象是JQuery自1.7之後提供的一個管理回調函數的工具,提供基本的增長、刪除、調用和禁用回調函數的能力。git

1.1 基本配置

Callbacks提供以下四個配置供開發者使用:github

  • once 保證全部回調函數只能執行一次。
  • memory 記錄上一次調用回調函數的參數值。若是在調用回調函數以後又經過add新回調函數,那麼會當即使用memory記錄的值調用新增的回調函數。
  • unique 保證每一個回調函數只能被添加一次,即回調函數列表中每一個函數都惟一。
  • stopOnFalse 若是一個回調函數返回false,則終止調用其餘剩餘會回調函數。

開發者能夠經過以上配置的字符串組合(以空格分隔)初始化回調對象。設計模式

var callbacks = $.Callbacks('unique memory');  //回調函數列表中每一個函數必須惟一,且要記錄上一次調用的參數值

1.2 基本API

Callbacks提供以下API:api

  1. callbacks.add()
    向回調函數列表中增長一個回調函數或回調函數集合。
  2. callbacks.remove()
    從回調函數列表中刪除一個回調函數或回調函數的集合。
  3. callbacks.disable()
    禁用改回調函數列表,不在執行任何動做。
  4. callbacks.disabled()
    回調函數列表是否被禁用。
  5. callbacks.empty()
    清空回調函數列表。
  6. callbacks.has()
    若是帶有參數,則表示該參數是否在回調函數列表中,不然表示回調函數列表是否爲空。
  7. callback.fire()
    指定參數調用列表中全部的回調函數。
  8. callback.fireWith()
    指定上下文和參數調用列表中全部的回調函數。
  9. callback.fired()
    回調函數是否被執行過。
  10. callback.lock()
    鎖定回調函數列表,使其保持當前的狀態不變。
  11. callback.locked()
    回調函數列表是否被鎖定。

2. 具體實現

2.1 add和fire/fireWith

從上面的API中已經能夠基本看出Callbacks的基本實現方式了,即維護一個回調函數列表(list)和一個參數隊列(queue)。每次經過add向list中增長回調函數,而後經過fire/fireWith從queue中取出參數按序執行回調函數。
JQuery的Callbacks模塊代碼很簡單,其核心就是一個內部的fire函數(非api中fire函數,請看下文fire介紹):數組

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

以上代碼摘自JQuery-1.12版本,代碼中的list就是回調函數列表,stack就是參數隊列queue。能夠看出,每次執行fire函數都會遍歷list,用傳進來的參數調用每一個回調函數,若是參數隊列不爲空,則遞歸的執行,一直到參數隊列取空爲止。app

咱們再看add函數的代碼:函數

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

add函數會根據參數類型將參數添加到回調函數列表中,即若是參數是一個函數(且要求unique),那麼就直接放進回調函數列表中,若是參數是一個類數組,那麼就遞歸調用add函數,將參數中的全部回調函數放進對調函數列表中。
請注意,若是在初始化Callbacks對象時設置了memory,那麼在調用add函數的時候,會使用memory記住的參數值調用每個新加的回調函數。工具

再看fire和fireWith函數:this

// 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函數將參數傳遞給回調函數。

2.2 disable和lock

此處要特別說明一下disable和lock的區別,先看這兩個函數的實現:

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

這兩個函數都禁用了回調函數隊列,即不能再經過fire/fireWith調用回調函數。可是,若是咱們在初始化Callbacks對象的時候,設置了memory的話,那麼當回調函數被lock以後,經過add新增的回調函數依然可使用memory的值調用。

3. 總結

不難看出,整個Callbacks的設計思想就是基於發佈訂閱(Pub/Sub)的觀察者模式。與通常實現方式相比較而言,一個對象再也不直接調用另外一個對象的方法,而是觀察另外一個對象的某個特定的任務或活動,這個觀察的對象就叫訂閱者(Subscriber),被觀察的對象就叫發佈者(Publisher),當發佈者的任務或活動完成時,會通知訂閱者。 這種設計模式的主要目的就是爲了達到程序之間的鬆耦合。

相關文章
相關標籤/搜索