Callbacks:回調對象,函數的一個統一管理(觀察者模式)
function test1() { console.log("test1 ..."); } function test2() { console.log("test2 ..."); } var cb = $.Callbacks(); cb.add(test1); cb.add(test2); cb.fire();
結果:json
test1 ...數組
test2 ...緩存
1.對不一樣做用域的函數進行統一的管理app
2.簡化統一調用函數
看下面的例子:this
var cb = $.Callbacks();//聲明一個全局的回調對象 function test1() { console.log("test1 ..."); } cb.add(test1); (function () { function test2() {//test2爲局部函數,外層中不能調用 console.log("test2 ..."); } cb.add(test2); })(); cb.fire()//簡化了調用方式
結果:spa
test1 ...對象
test2 ...遞歸
參數說明:索引
1.once:使回調函數只能被調用一次,如:
cb.fire();
cb.fire();//第二次調用回調函數將無效
2.memory:無視添加回調函數的順序,如:
cb.add(test1);
cb.fire();//test1/test2都將會執行
cb.add(test2);
3.unique:去除重複的回調函數,如:
cb.add(test1);
cb.add(test1);
cb.fire();//test1只會調用一次
4.stopOnFalse:當添加的回調函數有返回false時,後續的回調函數將不執行
cb.add(test1);//假設test1()爲return false
cb.add(test2);
cb.fire();//將只會執行到test1,test2不會執行
5.此外,Callbacks還支持多個參數共同傳遞,如Callbacks('memory once')
參數處理:
var optionsCache = {};//options的緩存對象
function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each(//遍歷options數組,進行處理 options.match( core_rnotwhite ) || [],//對options進行空格的切分 function( _, flag ) { object[ flag ] = true;//對單個option進行存放 } ); return object; }
options = typeof options === "string" ?//判斷options是字符串 ( optionsCache[ options ]//讀取緩存options || createOptions( options )//建立緩存對象 ) : jQuery.extend( {}, options );//options爲空時,合併類數組,防止出現undefined
Callbacks('memory once')
1.options的狀況:createOptions( options )返回的是{once:true, memory:true}
2.optionsCache的狀況:optionsCache的值爲{'once memory':{once:true, memory:true}}
3.options從新賦值:先根據options取optionsCache的值,爲空則建立一個options
1.false狀況:false null undefined 0 '' NaN
2.true狀況:包括對象、數組、正則、函數等。注意 '0'、'null'、'false'、{}、[]也都是true
3.未定義的變量直接判斷true/false程序報錯
私有變量:
1.memeroy:最後一次觸發回調時傳的參數
2.fired:列表中的函數是否已經回調至少一次
3.firing:列表中的回調函數是否正在被回調中
4.firingStart:回調的起點
5.firingLength:須要fire的隊列長度
6.firingIndex:當前正在firing的回調的隊列的索引
7.list:[],定義回調函數列表
8.stack:!options.once&&[],當options有once屬性時stack爲[](主要用來保存當前正在執行回調函數時的函數,),不然爲true
私有方法:在這裏真正執行須要回調的函數
fire = function( data ) {//data= [context, [arg1, arg2, arg3, ...]],context=this memory = options.memory && data;//判斷options是否有memory參數,無爲undefined,不然爲data fired = true; firingIndex = firingStart || 0;//賦值firingIndex firingStart = 0;//初始爲0 firingLength = list.length;//回調列表長度 firing = true;//true表示回調函數正在執行 for ( ; list && firingIndex < firingLength; firingIndex++ ) {//遍歷回調列表 // data[ 0 ]是函數執行的上下文,也就是平時的this // 這裏看再看下 self.fireWith 傳過來的參數 args 的格式 // 若是是stopOnFalse管理器,而且回調返回值是false,中斷! // list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) 是最終的執行回調的方法 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//options中的stopOnFalse在這做用,執行帶有false的回調函數而且有stopOnFalse參數,break出循環 memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) {//options爲stopOnFalse和memory的狀況 list = []; } else { self.disable(); } } },
注意:建立Callbacks返回的是self,而self是一個json,存放Callbacks的公有方法
公有方法
1.add
// Actual callback list list = [],//回調函數列表
add: function() { if ( list ) {//list爲空是,爲ture // First, we save the current length var start = list.length;//保存回調函數列表的長度 (function add( args ) {//add自執行函數 jQuery.each( args, function( _, arg ) {//遍歷參數,args=arguments var type = jQuery.type( arg );//判斷參數的類型 if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) {//options中的unique在這做用,判斷建立Callbacks是否有unique,取反,後判斷回調函數是否在回調列表中 //1.options.unique爲true,則爲false,arg在list爲true,反爲false,不push進list中,反之 //2.options.unique爲undefined ,則爲true,不判斷唯一性,直接push list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) {//cb.add([test2,test1])這種狀況 // Inspect recursively add( arg );//進行遞歸調用 } }); })( arguments );//將arguments傳進add // Do we need to add the callbacks to the // current firing batch? if ( firing ) {//若是當前在 firing 當中,那就把須要firing的長度設置成列表長度 firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) {//options中的memory在這做用,add後在次調用了fire //初始memory爲空,則爲false,當進行一次fire後,memory判斷爲true,調用fire,執行回調函數 firingStart = start; fire( memory ); } } return this; },
3.fire():執行回調列表中的回調方法,調用了另外一個公有方法,並傳遞this和參數列表
fire: function() {//調用回調函數 self.fireWith( this, arguments );//支持cb.fire('hello'); return this; },
4.fireWith():帶有參數的調用私有的fire()
// 以給定的上下文和參數調用全部回調函數 fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) {//options中的once在這做用,有once時,stack爲false,當fire一次後,fired爲true,因此第二次fire不執行 args = args || []; args = [ context, args.slice ? args.slice() : args ]; // 把 args 組織成 [context, [arg1, arg2, arg3, ...]] // function aaa() { // console.log(111) // cb.fire(); // } // function bbb() { // console.log(222) // } // var cb = $.Callbacks(); // cb.add(aaa); // cb.add(bbb); // cb.fire();這種狀況 if ( firing ) {// 若是當前還在 firing, stack.push( args ); // 將參數推入堆棧,等待當前回調結束再調用 } else { fire( args );//args包含this和arguments } } return this; },
5.empty()
empty: function() {//清空回調列表 list = []; firingLength = 0; return this; },
6.disable()
// 禁用回調列表中的回調 // 禁用掉以後,把裏邊的隊列、棧等所有清空了!沒法再恢復了,再次調用fire()無效 disable: function() { list = stack = memory = undefined; return this; },
7.disabled():判斷是否禁止,當調用disable()後,list爲undefined,取反
disabled: function() { return !list; },
8.lock()
lock: function() {//鎖住數組,禁止第二次fire(), stack = undefined;//清空棧,fire()一次後fired=true,fireWith()中( !fired || stack ) 爲false if ( !memory ) { self.disable(); } return this; },
9.locked():lock後stack爲undefined,取反,當options中有once參數,後續lock操做沒意義
locked: function() {//list是否被鎖住,返回一個鎖標誌 return !stack; },
10.remove():刪除回調列表,支持多個回調函數一塊兒刪除
remove: function() {//刪除回調列表 if ( list ) { jQuery.each( arguments, function( _, arg ) {//遍歷參數列表 var index; while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { // splice(index,howmany) 方法向/從數組中添加/刪除項目,而後返回被刪除的項目 // index -- 必需。整數,規定添加/刪除項目的位置 // howmany -- 必需。要刪除的項目數量。若是設置爲 0,則不會刪除項目 // 從回調隊列中移除當前查找到的這個方法 list.splice( index, 1 ); // Handle firing indexes // 在函數列表處於firing狀態時,最主要的就是維護firingLength和firgingIndex這兩個值 // 保證fire時函數列表中的函數可以被正確執行,防止取到(fire中的for循環須要這兩個值 // function aaa() { // console.log(1); // cb.remove(bbb); // } if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; },