jquery源碼解析:jQuery工具方法Callbacks詳解

來源:互聯網
上載者:User

標籤:style   ar   io   color   使用   sp   for   on   art   

我們首先來講下Callbacks是如何使用的:第一個例子

function a(){}

function b(){}

var cb = $.Callbacks();

cb.add(a);

cb.add(b);

cb.fire();      //就會先執行a方法,再執行b方法

上面大概的意思是:add方法,就是把方法添加到數組list中,每執行一次,就添加一個方法。而fire方法,就是把list數組中的方法按順序執行。

使用情境:第二個例子

function a(n){ }

(function(){

  function b(n){}

})

在這裡你無法調用b方法,因為b方法是局部變數。因此,你可以使用Callbacks來解決這個問題。

function a(n){}

var cb = $.Callbacks();

cb.add(a);

(function(){

  function b(n){}

  cb.add(b);

})

cb.fire("hello");

這時,b方法即使是局部變數,也可以通過cb.fire()進行調用。而且fire中的參數"hello",也可以傳入a,b方法。

Callbacks 可以傳4個值,jQuery.Callbacks(options),options有四個值:once,memory,unique,stopOnFalse 。

在第一個例子中,假設我們調用兩次cb.fire(); 那麼第一次會執行a,b方法,第二次也會調用a,b方法。但是如果你定義cb時這樣,var cb = $.Callbacks("once");那麼只有第一次會執行a,b方法,第二次不會執行。once的意思是只執行list數組中的方法一次,之後你調用fire,不會執行list數組中的方法。

memory的作用是:

var cb = $.Callbacks();

cb.add(a);

cb.fire();

cb.add(b);

針對以上代碼,方法a會執行,b方法不會執行。但是你在定義cb時,var cb = $.Callbacks("memory");這時,a,b方法都會執行。它具有記憶性。

unique的作用:去重,add如果添加相同的方法,在沒有unique的情況下,添加幾個相同的方法,就會執行幾次相同的方法,但是你傳入了unique,不管你添加多少次相同的方法,它都只會執行一次。

stopOnFalse的作用:

function a(){   return false;}

function b(){}

var cb = $.Callbacks();

cb.add(a);

cb.add(b);

cb.fire(); 

方法a,b都會執行。但是如果你定義cb時,var cb = $.Callbacks("stopOnFalse");只會執行a方法。因為a方法返回了false,而stopOnFalse的作用就是當方法返回false時,就停止迴圈list。因此b方法就不會執行了。

當然這四種參數,你可以組合使用,比如:var cb =  $.Callbacks("once memory");

接下來,源碼解析:由於代碼比較多,因此給大家一個線索,$.Callbacks -> return self; -> self.add(a) -> list.push(a) -> self.add(b) -> list.push(b) -> self.fire() ->self.fireWith -> 私人方法fire() -> 迴圈list數組,執行裡面的a,b方法。請按照此順序看代碼,然後再根據4個參數分別代表什麼意思,再按順序看一次。裡面有詳細的代碼解釋。

var optionsCache = {};

function createOptions( options ) {
  var object = optionsCache[ options ] = {};    //optionsCache[once] = {}
  jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {  

    //core_rnotwhite = /\S+/g, 取字元,options.match( core_rnotwhite ) = ["once"],此正則是為了處理多個值時,比如"once match"變成["once","memory"]
    object[ flag ] = true;      //_是index值0,flag是value值"once"
  });
  return object;     //optionsCache[once] = { "once":true }
}

jQuery.Callbacks = function( options ) {    //options有四個值:once,memory,unique,stopOnFalse 

  options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options );

  //當傳入參數時,就是字串,比如:once,那麼就會先去optionsCache中取once屬性,如果之前沒有此值,就調用createOptions(once)方法

  var memory,
    fired,
      firing,
        firingStart,
          firingLength,
            firingIndex,
              list = [],
                stack = !options.once && [],  //如果有once,那麼stack就是false,如果沒有,那麼stack就死[],if條件裡面空數組為真,因此在fire()->fireWith()時,就可以再次執行list數組中的方法了。
                  fire = function( data ) {     //這裡才是真正執行list數組中方法的地方
                    memory = options.memory && data;
                    fired = true;
                    firingIndex = firingStart || 0;
                    firingStart = 0;
                    firingLength = list.length;
                    firing = true;     //正在觸發list數組中的方法
                    for ( ; list && firingIndex < firingLength; firingIndex++ ) {
                      if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {

                      //如果回調方法返回false,並且有傳入stopOnFalse參數,就會停止for迴圈,停止執行list數組中後面的方法
                        memory = false;
                        break;
                      }
                    }
                    firing = false;   //方法執行結束
                    if ( list ) {       //當list數組中的方法執行結束後,就會判斷stack是否有值
                      if ( stack ) {
                        if ( stack.length ) {
                          fire( stack.shift() );   //如果有值,就會再次觸發fire執行
                        }
                      } else if ( memory ) {   //如果有once,stack就是false,就會走else
                        list = [];     //如果有傳入了memory,只會清空list數組。舉個例子:var cb = $.Callbacks("once memory");cb.add(a),cb.fire();cb.add(b);cb.fire()。因為有once,所以只會執行一次,因此第二個fire沒用。又由於有memory,所以a,b方法都會執行一次。如果這裡沒有memory,就會執行下面的else語句,這時只會執行a方法,b方法也不會執行。

                      } else {     
                        self.disable();   //阻止後面的所有fire操作
                      }
                    }
                  },
  self = {
    add: function() {    //此add方法,就是往list數組中push方法(fire時調用的方法)
      if ( list ) {       //[]為真
        var start = list.length;
        (function add( args ) {   
          jQuery.each( args, function( _, arg ) {
            var type = jQuery.type( arg );
            if ( type === "function" ) {    //如果是方法就push到list中
              if ( !options.unique || !self.has( arg ) ) {   //如果有唯一判斷,比如傳入unique,就必須判斷list數組中是否有此方法了,如果沒有,才push到list中。self.has(arg)方法就是判斷list數組中是否有arg方法。
                list.push( arg );
              }
            } else if ( arg && arg.length && type !== "string" ) {
              add( arg );   //此時add([a,b])是立即執行方法add而不是self.add。
            }
          });
        })( arguments );     //這裡處理add(a,b),add([a,b]),同時添加多個方法的情況
        if ( firing ) {
          firingLength = list.length;
        } else if ( memory ) {   //第一次調用add時,memory是undefined。當調用fire時,如果你傳入了memory,則memory就會變成true,這時你再調用add,就會進入if語句,進行fire。所以你傳入memory,fire後,再調用add(b),b方法會執行。
          firingStart = start;
          fire( memory );
        }
      }
      return this;
    },
    remove: function() {    //去掉list數組中相應的方法
      if ( list ) {
        jQuery.each( arguments, function( _, arg ) {
          var index;
          while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
            list.splice( index, 1 );        //主要通過數組的splice方法去掉
            if ( firing ) {
              if ( index <= firingLength ) {
                firingLength--;
              }
              if ( index <= firingIndex ) {
                firingIndex--;
              }
            }
          }
        });
      }
      return this;
    },
    has: function( fn ) {   //jQuery.inArray( fn, list ),如果fn在list中就返回1,不在,就返回-1
      return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );   //如果不傳入值,就判斷list中是否有值,有值就返回true。
    },
    empty: function() {
      list = [];
      firingLength = 0;
      return this;
    },
    disable: function() {    //後面所有的操作都失效
      list = stack = memory = undefined;
      return this;
    },
    disabled: function() {  
      return !list;
    },
    lock: function() {  //只會鎖住後面的fire操作,讓cb.fire方法失效。
      stack = undefined;
      if ( !memory ) {
        self.disable();
      }
      return this;
    },
    locked: function() {
      return !stack;
    },
    fireWith: function( context, args ) {     //這裡執行list數組中的方法
      if ( list && ( !fired || stack ) ) {   //第一次調用時fired是undefined,第二次調用時,fired為true,就要看stack了。
        args = args || [];        //如果fire中傳了值,就是args,如果沒傳,就是空數組 
        args = [ context, args.slice ? args.slice() : args ];
        if ( firing ) {   //當for迴圈在執行list數組中的方法時,firing為真,這時調用fire()->fireWith的話,只會把args添加到stack中。當list數組中的方法執行完之後。就會根據stack中是否有值來進行處理。舉個例子:var cb = $.Callbacks();function a(){ cb.fire()};funciton b(){};cb.add(a,b);cb.fire();執行a方法,這時又觸發fire(),但是不會再次執行a方法,因為這時firing為true,所以只會把stack加1,而是先等b方法執行結束後,才會又重新執行a方法。私人fire方法中會判斷stack的值,如果有值,就會繼續迴圈list數組中的方法進行調用執行。
          stack.push( args );

        } else {
          fire( args );        //真正執行list數組中的方法是私人方法fire。
        }
      }
      return this;
    },
    fire: function() {       //fire方法其實就是執行list中的方法
      self.fireWith( this, arguments );   //它調用的是fireWith方法.arguments就是fire中傳入的參數,它可以給list數組中的方法傳值進去
      return this;
    },
    fired: function() {
      return !!fired;   //只要調用過一次fire就會返回true
    }
  };

  return self;   //返回的是self對象。當調用var cb = $.Callbacks(),cb =self。所以調用cb.add,fire,其實就是self.add和fire方法
};

 

 

加油!

jquery源碼解析:jQuery工具方法Callbacks詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.