Callbacks即回調函數集合,在jQeury中配合異步操做實現異步回調任務,同時也具有函數解耦的基礎特性。簡單的說就是緩存回調函數的集合,因此這個方法應該具有的兩個基礎功能就是向集合中添加回調函數和觸發回調函數的方法。先來看一下jQuery的這兩個方法的使用:html
1 function fon1(){ 2 console.log("函數一"); 3 } 4 function fon2(a){ 5 console.log("函數二"); 6 } 7 var call = $.Callbacks();//建立回調函數對象 8 call.add(fon1,fon2);//添加回調函數 9 call.fire();//執行回調函數
基於這樣的基本功能模仿實現Callbacks的功能:數組
1 function Callback(){ 2 var list = []; 3 var self = { 4 add:function(){ 5 if(arguments.length > 0){ 6 for(var ele in arguments){ 7 list.push(arguments[ele]); 8 } 9 } 10 return this; 11 }, 12 fire:function(){ 13 if(list.length > 0){ 14 for(var ele in list){ 15 list[ele](); 16 } 17 } 18 return this; 19 } 20 } 21 return self; 22 }
上面的簡單模仿能夠實現添加回調函數和執行回調函數,接着來看jQuery實現的不一樣模式的回調列表對象。緩存
1. 若是Callback只是上面模仿的那樣的話,那就等因而回調函數裸奔,若是有需求是回調函數只能被觸發一次,也就是說重複調用fire無效,要實現這個功能只須要在回調函數執行完之後將回調函數列的值修改爲undefined便可,而後再在添加和執行函數上加一個判斷阻斷執行就能夠。下面是修改事後的代碼:app
1 //Callback源碼 2 function Callback(options){ 3 //list爲undefined的時候(函數列表被禁用),主動觸發disable禁用 4 //once模式下回調函數列表執行一輪後禁用回調函數列表 5 var list = []; 6 options = options || ""; 7 var fire = function(data){ 8 //實現回調函數執行的方法 9 if(list.length > 0){ 10 for(var ele in list){ 11 list[ele].apply(data[0],data[1]); 12 } 13 } 14 if(options.indexOf("once") != -1){ 15 //清空函數列表,實現once只能調用fire執行一次的功能 16 list = undefined; 17 } 18 } 19 var self = { 20 add:function(){ 21 if(list){//函數被鎖定或者禁用就不能再添加回調函數了 22 if(arguments.length > 0){ 23 for(var ele in arguments){ 24 list.push(arguments[ele]); 25 } 26 } 27 } 28 return this; 29 }, 30 disable:function(){//禁用回調函數 31 list = undefined; 32 return this; 33 }, 34 disabled:function(){//查看回調函數是否被禁用 35 return !list; 36 }, 37 fireWith:function(context,args){ 38 //由於fire做爲做用域上的方法this指向window或者undefined 39 //這裏將this指向繼續指向self,而後將類數組的args轉換成數組(fireWith的功能) 40 if(list){ 41 args = args || []; 42 var data = [context,args.slice ? args.slcie() : args]; 43 fire(data); 44 } 45 return this; 46 }, 47 fire:function(){ 48 //fire做爲回調函數執行的API 49 self.fireWith(this,arguments); 50 return this; 51 } 52 } 53 return self; 54 }
實現「once」模式的回調函數列表之際代碼只在14行~17行,而後add和fireWith方法內加一層判斷便可。在實現「once」模式的時候考慮到後面還有其餘模式和一些回調函數對象工具方法的實現,將fire()方法模仿jQuery源碼提取到了對象做用域做爲普通方法,回調函數對象上保留操做接口只負責調用執行,fireWith()用來調整執行的上下文和參數類型。順便還實現了一個禁用回調函數方法disble方法。異步
2. 接着來看看第二個回調函數模式「memory」以及「once memory」的混合模式回調函數對象的實現,「memory」模式就是在觸發一次函數列表之後,後面添加的函數會被自動觸發,這個實現很是的簡單,只要記錄一下上一次執行的列表的最後一個回調函數的下標,不過要注意的是,若是是"once memory"是混合模式須要緩存參數,還有下標要置爲0。具體代碼以下,模仿的代碼相對於jQuery源碼中的Callbacks工具方法表面邏輯清晰一些,可是沒有像jQuery源碼那樣極致的避免冗餘,因此若是是學習源碼的最優設計方案而非Callbacks的設計結構請繞路。函數
1 //Callback源碼 2 function Callback(options){ 3 //list爲undefined的時候(函數列表被禁用),主動觸發disable禁用 4 //once模式下回調函數列表執行一輪後禁用回調函數列表 5 var list = []; 6 options = options || ""; 7 var fired = false; 8 var fireStart; 9 var fireIndex; 10 var memoryData = null; 11 var fire = function(data){ 12 fireIndex = fireStart || 0; 13 fireStart = 0; 14 //實現回調函數執行的方法 15 if(list.length > 0){ 16 for(;fireIndex < list.length; fireIndex++){ 17 list[fireIndex].apply(data[0],data[1]); 18 } 19 } 20 if(options.indexOf("once") != -1){ 21 //清空函數列表,實現once只能調用fire執行一次的功能 22 list = undefined; 23 fireIndex = 0; 24 } 25 if(options.indexOf("memory") != -1){ 26 fireStart = fireIndex; 27 fired = true; 28 list = list || []; 29 memoryData = data; 30 } 31 32 } 33 var self = { 34 add:function(){ 35 if(list){//函數被鎖定或者禁用就不能再添加回調函數了 36 if(arguments.length > 0){ 37 for(var ele in arguments){ 38 list.push(arguments[ele]); 39 } 40 } 41 } 42 if(options.indexOf("memory") != -1 && fired){ 43 fire(memoryData); 44 } 45 return this; 46 }, 47 disable:function(){//禁用回調函數 48 list = undefined; 49 return this; 50 }, 51 disabled:function(){//查看回調函數是否被禁用 52 return !list; 53 }, 54 fireWith:function(context,args){ 55 //直接調用API時表示fire()非add觸發,因此fireStart要設置爲0 56 fireStart = 0; 57 //由於fire做爲做用域上的方法this指向window或者undefined 58 //這裏將this指向繼續指向self,而後將類數組的args轉換成數組(fireWith的功能) 59 if(list){ 60 args = args || []; 61 var data = [context,args.slice ? args.slcie() : args]; 62 fire(data); 63 } 64 return this; 65 }, 66 fire:function(){ 67 //fire做爲回調函數執行的API 68 self.fireWith(this,arguments); 69 return this; 70 } 71 } 72 return self; 73 }
在第二部分主要是是實現了once、memory模式,再在這個基礎上實現unique、stopOnFalse模式,原本項將remove、lock等這些回調對象的工具方法都實現,我仍是放棄了,由於太簡單了,實現起來又會佔用不少頁面,不方便理解更核心的代碼,因此下面是在第二部分的基礎上添加了unique、stopOnFalse模式的所有代碼:工具
1 //Callback源碼 2 function Callback(options){ 3 //list爲undefined的時候(函數列表被禁用),主動觸發disable禁用 4 //once模式下回調函數列表執行一輪後禁用回調函數列表 5 var list = []; 6 options = options || ""; 7 var fired = false; 8 var fireStart; 9 var fireIndex; 10 var memoryData = null; 11 var memory = true; 12 //stopOnFalse 模式表示中斷執行,即便"memory stopOnFalse"混合模式,後面添加方法也不會觸發執行,而必須callback.fire()方法來觸發。 13 var stopOnFalse = options.indexOf("stopOnFalse") != -1 ? true : false; 14 var fire = function(data){ 15 fireIndex = fireStart || 0; 16 fireStart = 0; 17 //實現回調函數執行的方法 18 if(list.length > 0){ 19 for(;fireIndex < list.length; fireIndex++){ 20 if( list[fireIndex].apply(data[0],data[1]) === false && stopOnFalse ){ 21 memory = false; 22 break; 23 } 24 } 25 } 26 if(options.indexOf("once") != -1){ 27 //清空函數列表,實現once只能調用fire執行一次的功能 28 list = undefined; 29 fireIndex = 0; 30 } 31 if(options.indexOf("memory") != -1 && memory){ 32 fireStart = fireIndex; 33 fired = true; 34 list = list || []; 35 memoryData = data; 36 } 37 } 38 var self = { 39 add:function(){ 40 if(list){//函數被鎖定或者禁用就不能再添加回調函數了 41 if(arguments.length > 0){ 42 if(options.indexOf("unique") != -1){//unique模式--不重複添加同一個函數 43 for(var ele in arguments){ 44 if(list.indexOf(arguments[ele]) == -1){ 45 list.push(arguments[ele]); 46 } 47 } 48 }else{ 49 for(var ele in arguments){ 50 list.push(arguments[ele]); 51 } 52 } 53 } 54 } 55 if(options.indexOf("memory") != -1 && fired){ 56 fire(memoryData); 57 } 58 return this; 59 }, 60 disable:function(){//禁用回調函數 61 list = undefined; 62 return this; 63 }, 64 disabled:function(){//查看回調函數是否被禁用 65 return !list; 66 }, 67 fireWith:function(context,args){ 68 //直接調用API時表示fire()非add觸發,因此fireStart要設置爲0 69 fireStart = 0; 70 //由於fire做爲做用域上的方法this指向window或者undefined 71 //這裏將this指向繼續指向self,而後將類數組的args轉換成數組(fireWith的功能) 72 if(list){ 73 args = args || []; 74 var data = [context,args.slice ? args.slice() : args]; 75 fire(data); 76 } 77 return this; 78 }, 79 fire:function(){ 80 //fire做爲回調函數執行的API 81 self.fireWith(this,arguments); 82 return this; 83 } 84 } 85 return self; 86 }
最後,這篇博客主要就是爲了實現了Callbacks的源碼,並不是對其應用進行解析,畢竟在不少文檔中都有很詳細的說明,若是以爲文檔不是很容易明白,能夠參考這篇博客:jQuery.Callbacks源碼及其應用分析學習