jQuery的回調管理機制

// 對option的一個緩存,避免每次都須要createOptions,option是建立Callback對象時的傳入的參數
// 每一個option被存入optionsCache中相似於{memory:true, once:true,...}
var optionsCache = {};ajax

// 將字符串表達轉成對象表達,並存在緩存中,一個內部使用的方法。
function createOptions( options ) {
  var object = optionsCache[ options ] = {};
  jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
    object[ flag ] = true;
  });
  return object;
}數組

/*緩存

 * Callback構造函數在構造對象時的一些參數選項app

 * 
 * once: 確保這個回調列表只執行一次(像一個遞延 Deferred).
 * memory: 保持之前的值和將添加到這個列表的後面的最新的值當即執行調用任何回調 (像一個遞延 Deferred).
 * unique: 確保一次只能添加一個回調(不能列表中的重複).
 * stopOnFalse: 當一個回調返回false 時中斷調用函數

*/this


// options參數以字符串的形式傳入,例如"unique memory",以空格分割,用來控制回調函數的行爲
// $.Callbacks是在jQuery內部使用,如爲$.ajax,$.Deferred等組件提供基礎功能的函數。它也能夠用在相似功能的一些組件中,如本身開發的插件。
jQuery.Callbacks = function( options ) {spa

  options = typeof options === "string" ?
  // 先檢測一下cache,若是爲空,則把options從字符串的形式轉變爲Object的形式
  // 經過字符串在optionsCache尋找有沒有相應緩存,若是沒有則建立一個,有則引用
  ( optionsCache[ options ] || createOptions( options ) ) :
  // 若是是對象則經過jQuery.extend擴展到新對象中賦值給options
  jQuery.extend( {}, options );插件

  var 
  // 最後一次觸發回調時傳的參數
  memory,
  // 列表中的函數是否已經回調至少一次
  fired,
  // 列表中的函數是否正在回調中 
  firing,
  // 回調的起點
  firingStart,
  // 回調時的循環結尾
  firingLength,
  // 當前正在回調的函數索引
  firingIndex,
  // 回調函數列表
  list = [],
  // 可重複的回調函數堆棧,用於控制觸發回調時的參數列表
  stack = !options.once && [],
  // 觸發回調函數列表對象

  //這裏的data參數究竟是啥???
  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++ ) {
      // 對當前回調函數在data[0]環境下,傳入data[1]參數,並執行,若是返回false,而且設置了stopOnFalse,則將memory設爲false並返回。
      if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {遞歸

        // 阻止將來可能因爲add所產生的回調,由於當memory爲true時,add進來的回調函數會當即被觸發
        memory = false; 

        //因爲參數stopOnFalse爲true,因此當有回調函數返回值爲false時退出循環
        break;
      }
    }
    //標記回調結束
    firing = false;
    //若是列表存在
    if ( list ) {
      //若是堆棧存在
      if ( stack ) {
        //若是堆棧不爲空
        if ( stack.length ) {
          //從堆棧頭部取出,遞歸fire。
          fire( stack.shift() );
        }
      //不然,若是有記憶
      } else if ( memory ) {
        //列表清空
        list = [];
      //再不然阻止回調列表中的回調
      } else {
        self.disable();
      }
    }
  },
  //  Callback構造函數返回的對象
  self = {
    // 向回調列表中添加一個回調或回調的集合。
    add: function() {
      if ( list ) {
        // 首先咱們存儲當前列表長度 主要用於當Callbacks傳入memory參數時
        var start = list.length;
        // 在作其它操做以前,先對args作一些處理
        (function add( args ) {
          // 對args傳進來的列表的每個對象執行操做
          jQuery.each( args, function( _, arg ) {
            var type = jQuery.type( arg );
            // 若是是函數
            if ( type === "function" ) {
              // 若是回調離別不容許重複,則看看列表中是否包含,若是不包含則加入列表
              if ( !options.unique || !self.has( arg ) ) {
                list.push( arg );
              }
            // 若是是類數組或對象,則遞歸調用add方法
            } else if ( arg && arg.length && type !== "string" ) {
              //遞歸 調用add方法往list中增長回調
              add( arg );
            }
          });
        })( arguments );
        // 若是整個callbacks對象中的callback正在執行時,回調時的循環結尾變成add以後的list長度長度,確保新增長的回調函數會被執行到
        if ( firing ) {
          firingLength = list.length;
        // 若是有memory,咱們馬上調用。 這裏的memory
        } else if ( memory ) {
          firingStart = start;
          fire( memory );
        }
      }
      return this;
    },
    // 從列表刪除回調函數
    remove: function() {
      if ( list ) {
        //繼續用jQuery.each,對arguments中的全部參數處理
        jQuery.each( arguments, function( _, arg ) {
          var index;
          //找到arg在列表中的位置
          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.inArray( fn, list ) > -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;};

相關文章
相關標籤/搜索