jq源碼學習9_Callbacks : 回調對象 : 對函數的統一管理

基於jquery-2.0.3的源碼學習

//9. Callbacks : 回調對象 : 對函數的統一管理
//once: 確保這個回調列表只執行一次(像一個遞延 Deferred).
//memory: 保持以前的值和將添加到這個列表的後面的最新的值立即執行調用任何回調 (像一個遞延 Deferred).
//unique: 確保一次只能添加一個回調(所以有沒有在列表中的重複).
//stopOnFalse: 當一個回調返回false 時中斷調用
var optionsCache = {};//建立json變量緩存
// 工具函數,將字符串格式得標記轉換爲對象格式得標記。
function createOptions(options){
  // 對象object用於存儲轉換後的標記對象,其屬性爲標記字符串,屬性值爲true。
  var object = optionsCache[options] = {};
  jQuery.each(options.match(core_rnotwhite) ||[],function( _, flag ){
    // 用空白符把標記字符串分割爲數組,然後遍歷數組,爲返回值object,添加單個標記,屬性值一律爲true
     object[flag] = true;
  });
   // 最後返回對象格式的標記
  return object;
}
//如果 jQuery.Callbacks的參數是 $.Callbacks('one memory')
//則options:{one:true,memory:true}
/*optionsCache:{
  'one memory':{one:true,memory:true}
}*/
 jQuery.Callbacks = function(options){
   // 解析字符串標記options爲對象
  // 如果options是字符串,調用工具函數createOptions(options)將標記字符串options解析爲標記對象,
  //否則調用jquery.extend()將options中的內容合併到一個空對象中
   //通過字符串在optionsCache尋找有沒有相應緩存,如果沒有則創建一個,有則引用
   //如果是對象則通過jQuery.extend深複製後賦給options。
   options = typeof options === "string"?
   ( optionsCache[ options ] || createOptions( options ) ) :
   jQuery.extend( {}, options );
   //聲明局部變量
   var 
   memory, // 最後一次觸發回調時傳的參數
   fired,// 列表中的函數是否已經回調至少一次
   firing,//列表中的函數是否正在回調中
   firingStart,// 回調的起點
   firingLength,// 回調時的循環結尾
   firingIndex,// 當前正在回調的函數索引
   list = [], // 回調函數列表
   stack = !options.once && [],// 可重複的回調函數堆棧,用於控制觸發回調時的參數列表
   fire = function( data ){ // 觸發回調函數列表
    //如果參數memory爲true,則記錄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; //阻止未來可能由於add所產生的回調
					break;
				}
      }
      firing = false;//標記回調結束
      if ( list ) {
				if ( stack ) {
					if ( stack.length ) {
              //從堆棧頭部取出,遞歸fire
						fire( stack.shift() );
					}
				} else if ( memory ) {//否則,如果有記憶
					list = [];
				} else {//再否則阻止回調列表中的回調
					self.disable();
				}
			}
   },
   self={  // 暴露在外的Callbacks對象,對外接口
      add:function(){ // 回調列表中添加一個回調或回調的集合
        if(list){
          //首先我們存儲當前列表長度
           var start = list.length;
           //進行匿名函數自執行
           (function add(args){
             //jQuery.each,對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"){
                   add(arg);
                }
              });
           })(arguments);
           //如果回調列表中的回調正在執行時,其中的一個回調函數執行了Callbacks.add操作
           // 上句話可以簡稱:如果在執行Callbacks.add操作的狀態爲firing時
           // 那麼需要更新firingLength值
           if(firing){
               firingLength = list.length;
           }else if(memory){
              //如果options.memory爲true,則將memory做爲參數,應用最近增加的回調函數
              firingStart = start;
              fire( memory );
           }
        }
        //return this就是返回當前對象的引用(就是實際調用這個方法的實例化對象)
        return this;
      },
      remove:function(){ // 從函數列表中刪除函數(集)
        if(list){
           jQuery.each(arguments,function( _, arg ){
                var index;
                 // while循環的意義在於藉助於強大的jQuery.inArray刪除函數列表中相同的函數引用(沒有設置unique的情況)
                // jQuery.inArray將每次返回查找到的元素的index作爲自己的第三個參數繼續進行查找,直到函數列表的盡頭
                // splice刪除數組元素,修改數組的結構
                while( (index = jQuery.inArray(arg,list,index) ) > -1){
                    list.splice( index,1 );
                    if(firing){
                       if(index <= firingLength){
                          firingLength -- ;
                       }
                       if(index <= firingIndex){
                           firingIndex --;
                       }
                    }
                }
           });
        }
        return this;
      },
     has:function(fn){// 回調函數是否在列表中.
         return fn ? jQuery.isArray( fn, list ,index ) > -1 : !!(list && list.length);
     },
     empty:function(){// 從列表中刪除所有回調函數
       list = [];
       firingLength = 0;
       return this;
     },
     disable:function(){// 禁用回調列表中的回調。
         list = stack = memory = undefined;
         return this;
     },
     disabled: function() {  //  列表中否被禁用
      return !list;
    }, 
    lock:function(){ // 鎖定列表
      stack = undefined;
      if(!memory){
         self.disable();
      }
      return this;
    },
    locked: function() {// 列表是否被鎖
      return !stack;
    },
    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;
    },
    fire: function() {// 以給定的參數調用所有回調函數
      self.fireWith( this, arguments );
      return this;
    },
    fired: function() {// 回調函數列表是否至少被調用一次
      return !!fired;
    }
   };
   return self;
 }
//9. Callbacks : 回調對象 : 對函數的統一管理

參考了https://zixuephp.net/manual-javascript-682.html

發佈了133 篇原創文章 · 獲贊 27 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章