回調對象(Callbacks object)模塊是JQuery中的一個很基礎的模塊,不少其餘的模塊(好比Deferred、Ajax等)都依賴於Callbacks模塊的實現。
本文主要描述回調對象的基本功能、具體實現,以及使用到的設計思想。jquery
從jquery的API 中能夠了解到,Callbacks對象是JQuery自1.7之後提供的一個管理回調函數的工具,提供基本的增長、刪除、調用和禁用回調函數的能力。git
Callbacks提供以下四個配置供開發者使用:github
開發者能夠經過以上配置的字符串組合(以空格分隔)初始化回調對象。設計模式
var callbacks = $.Callbacks('unique memory'); //回調函數列表中每一個函數必須惟一,且要記錄上一次調用的參數值
Callbacks提供以下API:api
從上面的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函數將參數傳遞給回調函數。
此處要特別說明一下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的值調用。
不難看出,整個Callbacks的設計思想就是基於發佈訂閱(Pub/Sub)的觀察者模式。與通常實現方式相比較而言,一個對象再也不直接調用另外一個對象的方法,而是觀察另外一個對象的某個特定的任務或活動,這個觀察的對象就叫訂閱者(Subscriber),被觀察的對象就叫發佈者(Publisher),當發佈者的任務或活動完成時,會通知訂閱者。 這種設計模式的主要目的就是爲了達到程序之間的鬆耦合。