jQuery封裝了不少關於異步操做的方法,好比$.ajax
$.Deferred
等等,這些方法都是以Callbacks爲基礎開發的,Callbacks一個多用途的回調列表對象,提供了強大的的方式來管理回調函數列表,他能夠管理一個列表,在你須要執行時候觸發,觸發時機由調用fire方法決定html
var cb = $.Callbacks();
cb.add(function(){
console.log('first function');
})
cb.add(function () {
console.log('second function');
})
cb.fire();
// first function
// second function
複製代碼
$.Callbacks()返回一個Callbacks對象,你能夠調用該對象的方法對函數列表進行操做,要實現這樣的功能很簡單,下面是一個簡易的實現ajax
function Callbakcs () {
var list = []
return {
add: function (func) {
list.push(func);
},
fire: function () {
var args = [].slice.call(arguments);
list.forEach(function(item) {
item.apply(null, arguments);
})
}
}
}
複製代碼
一樣的功能,只不過jQuery支持了不一樣的調用方式數組
在調用$.Callbacks的時候,咱們能夠傳入一個參數用來改變回調列表的行爲緩存
// 支持兩種傳參方式,一種對象類的,一種字符串類的,可組合使用,傳入字符串的時候會對字符串進行處理轉換爲對象
var cb = $.Callback("once unique");
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
function createOptions( options ) {
var object = {};
jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
object[ flag ] = true;
} );
return object;
}
// 函數內部存儲了一個options對象,最後會是相似{once: true, unique: true}這樣的對象
複製代碼
在函數內部咱們還能看到其餘一些變量bash
var // 標記是否正在觸發
firing,
// 緩存的上一次調用時候的參數和this指向
memory,
// 標記是否已經調用過fire
fired,
// 防止調用的標記
locked,
// 回調列表
list = [],
// 參數和this指向的列表
queue = [],
// 標記當前執行到哪一個回調
firingIndex = -1,
// 執行回調列表
fire = function () {}
// 暴露的對象
self = {}
複製代碼
add方法主要會判斷是不是unique、memory模式、unique遇到相同的回調函數回過濾掉,memory會檢查fired是否爲true,若是是會將memory裏面的參數添加到queue隊列中,最後調用fireapp
if ( memory && !firing ) {
firingIndex = list.length - 1;
queue.push( memory );
}
if ( memory && !firing ) {
firingIndex = list.length - 1;
queue.push( memory );
}
( function add( args ) {
jQuery.each( args, function( _, arg ) {
if ( isFunction( arg ) ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && toType( arg ) !== "string" ) {
// Inspect recursively
add( arg );
}
} );
} )( arguments );
複製代碼
添加的是否用到了遞歸,由於支持了傳入數組的方法異步
self裏面會調用fireWith方法,咱們在調用fire的時候其實是調用的這個方法函數
fire: function() {
self.fireWith( this, arguments );
return this;
},
fireWith: function( context, args ) {
if ( !locked ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
// 降參數和this對象緩存到queue裏面
queue.push( args );
if ( !firing ) {
fire();
}
}
return this;
},
複製代碼
真正的fire會遍歷取出queue裏面的參數,緩存到memory裏面,若是不是memory模式則會在後面清除掉,遍歷list,用memory裏的參數調用各個方法ui
for ( ; queue.length; firingIndex = -1 ) {
memory = queue.shift();
while ( ++firingIndex < list.length ) {
// 若是傳入stopOnFalse,且返回false,當即中止遍歷
if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
options.stopOnFalse ) {
firingIndex = list.length;
memory = false;
}
}
}
複製代碼
Callback部分的代碼比較簡單,固然這部分都是同步邏輯,callback是Deffered的基礎,因此須要先明白一下原理this