欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

談?wù)刯Query之Deferred源碼剖析

 更新時間:2016年12月19日 08:27:37   作者:猴子  
這篇文章主要介紹了談?wù)刯Query之Deferred源碼剖析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

一、前言

大約在夏季,我們談過ES6的Promise,其實在ES6前jQuery早就有了Promise,也就是我們所知道的Deferred對象,宗旨當然也和ES6的Promise一樣,通過鏈式調(diào)用,避免層層嵌套,如下:

//jquery版本大于1.8
function runAsync(){
  var def = $.Deferred();
  setTimeout(function(){
    console.log('I am done');
    def.resolve('whatever');
  }, 1000);
  return def;
}
runAsync().then(function(msg){
  console.log(msg);//=>打印'whatever'
}).done(function(msg){
  console.log(msg);//=>打印'undefined'
});

注:從jQuery1.8版本開始,then方法會返回一個新的受限制的deferred對象,即deferred.promise()—后續(xù)源碼解讀中我們會更加全面地了解到。因此,上述代碼done中會打印'undefined'。

好了,通過上述示例代碼,短暫的回顧了jQuery的Deferred使用后,我們一起來看看jQuery是怎么實現(xiàn)Deferred,當然解讀jQuery的版本是大于1.8。

二、jQuery之Deferred源碼剖析

整體架構(gòu),如下:

jQuery.extend( {
  Deferred: function( func ) {
    var tuples = [
        // action, add listener, listener list, final state
        [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ],
        [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ],
        [ "notify", "progress", jQuery.Callbacks( "memory" ) ]
      ],
      state = "pending",
      promise = {
        state: function() {...},
        always: function() {...},
        then: function() {...},
        promise: function( obj ) {...}
      },
      deferred = {};
    // Keep pipe for back-compat
    promise.pipe = promise.then;
    // Add list-specific methods
    jQuery.each( tuples, function( i, tuple ) {} );
    // Make the deferred a promise
    promise.promise( deferred );
    // Call given func if any
    if ( func ) {
      func.call( deferred, deferred );
    }
    // All done!
    return deferred;
  }
}

整體架構(gòu)上,如果你了解設(shè)計模式中的工廠模式,那么不難看出,jQuery.Deferred就是一個工廠,每次執(zhí)行jQuery.Deferred時,都會返回一個加工好的deferred對象。

接下來,我們再一步一步剖析上述代碼。

首先,是數(shù)組tuples:

tuples = [
  // action, add listener, listener list, final state
  [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ],
  [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ],
  [ "notify", "progress", jQuery.Callbacks( "memory" ) ]
]

tuples一開始就為我們預(yù)先定義了三種狀態(tài)—‘resolved'、'rejected'以及'pending',以及它們所對應(yīng)的一系列值和操作,值得注意的是每種狀態(tài)中,都調(diào)用了一個jQuery.Callbacks方法,如下:

它是個什么玩意兒?

jQuery.Callbacks = function( options ) {

  // Convert options from String-formatted to Object-formatted if needed
  // (we check in cache first)
  options = typeof options === "string" ?
    createOptions( options ) :
    jQuery.extend( {}, options );

  var // Flag to know if list is currently firing
    firing,

    // Last fire value for non-forgettable lists
    memory,

    // Flag to know if list was already fired
    fired,

    // Flag to prevent firing
    locked,

    // Actual callback list
    list = [],

    // Queue of execution data for repeatable lists
    queue = [],

    // Index of currently firing callback (modified by add/remove as needed)
    firingIndex = -1,

    // Fire callbacks
    fire = function() {

      // Enforce single-firing
      locked = options.once;

      // Execute callbacks for all pending executions,
      // respecting firingIndex overrides and runtime changes
      fired = firing = true;
      for ( ; queue.length; firingIndex = -1 ) {
        memory = queue.shift();
        while ( ++firingIndex < list.length ) {

          // Run callback and check for early termination
          if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
            options.stopOnFalse ) {

            // Jump to end and forget the data so .add doesn't re-fire
            firingIndex = list.length;
            memory = false;
          }
        }
      }

      // Forget the data if we're done with it
      if ( !options.memory ) {
        memory = false;
      }

      firing = false;

      // Clean up if we're done firing for good
      if ( locked ) {

        // Keep an empty list if we have data for future add calls
        if ( memory ) {
          list = [];

        // Otherwise, this object is spent
        } else {
          list = "";
        }
      }
    },

    // Actual Callbacks object
    self = {

      // Add a callback or a collection of callbacks to the list
      add: function() {
        if ( list ) {

          // If we have memory from a past run, we should fire after adding
          if ( memory && !firing ) {
            firingIndex = list.length - 1;
            queue.push( memory );
          }

          ( function add( args ) {
            jQuery.each( args, function( _, arg ) {
              if ( jQuery.isFunction( arg ) ) {
                if ( !options.unique || !self.has( arg ) ) {
                  list.push( arg );
                }
              } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {

                // Inspect recursively
                add( arg );
              }
            } );
          } )( arguments );

          if ( memory && !firing ) {
            fire();
          }
        }
        return this;
      },

      // Remove a callback from the list
      remove: function() {
        jQuery.each( arguments, function( _, arg ) {
          var index;
          while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
            list.splice( index, 1 );

            // Handle firing indexes
            if ( index <= firingIndex ) {
              firingIndex--;
            }
          }
        } );
        return this;
      },

      // Check if a given callback is in the list.
      // If no argument is given, return whether or not list has callbacks attached.
      has: function( fn ) {
        return fn ?
          jQuery.inArray( fn, list ) > -1 :
          list.length > 0;
      },

      // Remove all callbacks from the list
      empty: function() {
        if ( list ) {
          list = [];
        }
        return this;
      },

      // Disable .fire and .add
      // Abort any current/pending executions
      // Clear all callbacks and values
      disable: function() {
        locked = queue = [];
        list = memory = "";
        return this;
      },
      disabled: function() {
        return !list;
      },

      // Disable .fire
      // Also disable .add unless we have memory (since it would have no effect)
      // Abort any pending executions
      lock: function() {
        locked = true;
        if ( !memory ) {
          self.disable();
        }
        return this;
      },
      locked: function() {
        return !!locked;
      },

      // Call all callbacks with the given context and arguments
      fireWith: function( context, args ) {
        if ( !locked ) {
          args = args || [];
          args = [ context, args.slice ? args.slice() : args ];
          queue.push( args );
          if ( !firing ) {
            fire();
          }
        }
        return this;
      },

      // Call all the callbacks with the given arguments
      fire: function() {
        self.fireWith( this, arguments );
        return this;
      },

      // To know if the callbacks have already been called at least once
      fired: function() {
        return !!fired;
      }
    };

  return self;
};

 

細細品味了上述jQuery.Callbacks源碼,如果你了解設(shè)計模式中的發(fā)布訂閱者模式,不難發(fā)現(xiàn),就是一個”自定義事件”嘛

所以,我們精簡jQuery.Callbacks后,核心代碼如下:

jQuery.Callbacks = function(){
  var list = [],
    self = {
      add: function(){/*添加元素到list*/},
      remove: function(){/*從list移除指定元素*/},
      fire: function(){/*遍歷list并觸發(fā)每次元素*/}
    };
  return self;
}

一目了然,我們每執(zhí)行一次jQuery.Callbacks方法,它就會返回一個獨立的自定義事件對象。在tuples每個狀態(tài)中執(zhí)行一次jQuery.Callbacks,也就豁然開朗了—為每個狀態(tài)提供一個獨立的空間來添加、刪除以及觸發(fā)事件。

好了,關(guān)于變量tuples,我們就算大致解讀完了。

state就是deferred對象的狀態(tài)值嘛,我們可以通過deferred.state方法獲?。ㄉ院髸姷剑?/p>

promise就是一個擁有state、always、then、promise方法的對象,每個方法詳解如下:

promise = {
  state: function() {//返回狀態(tài)值
    return state;
  },
  always: function() {//不管成功還是失敗,最終都會執(zhí)行該方法
    deferred.done( arguments ).fail( arguments );
    return this;
  },
  then: function( /* fnDone, fnFail, fnProgress */ ) {...},//重頭戲,稍后會詳講
  promise: function( obj ) {//擴展promise,如不久我們會看見的promise.promise( deferred );
    return obj != null ? jQuery.extend( obj, promise ) : promise;
  }
}

隨后聲明的一個空對象deferred。

promise.pipe=promise.then,就不累贅了,下面我們來看看jQuery.each(tuples, function(i, tuple){…})都干了什么,源碼如下:

/*
tuples = [
  [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ],
  [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ],
  [ "notify", "progress", jQuery.Callbacks( "memory" ) ]
]
*/
jQuery.each( tuples, function( i, tuple ) {
  var list = tuple[ 2 ],
    stateString = tuple[ 3 ];
  // promise[ done | fail | progress ] = list.add
  promise[ tuple[ 1 ] ] = list.add;
  // Handle state
  if ( stateString ) {
    list.add( function() {
      // state = [ resolved | rejected ]
      state = stateString;
    // [ reject_list | resolve_list ].disable; progress_list.lock
    }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
  }
  // deferred[ resolve | reject | notify ]
  deferred[ tuple[ 0 ] ] = function() {
    deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
    return this;
  };
  deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
} );

通過jQuery.each遍歷tuples數(shù)組,并對其進行相關(guān)操作,比如我們拿tuples數(shù)組中的第一個元素舉例:

['resolve', 'done', jQuery.Callbacks('once memory'), 'resolved']

第一步、聲明的變量list指向jQuery.Callbacks返回的對象,stateString取值為'resolved'

第二步、為promise添加'done'屬性,并指向第一步中l(wèi)ist.add(fail和progress即指向?qū)儆诟髯缘淖远x事件對象)

第三步、判斷stateString值,如果為'resolved'或'rejected'狀態(tài),那么就添加三個事件函數(shù)到對應(yīng)的list列表中:

  • --改變state狀態(tài)的函數(shù)
  • --禁止對應(yīng)狀態(tài)的處理,如'resolved'后,那么必定不會觸發(fā)rejected狀態(tài)咯,反之亦然
  • --禁止pending狀態(tài),都'resolved'或者'rejected'了,那么deferred肯定不會處于pending狀態(tài)咯

第四步、為對象deferred,添加觸發(fā)各自狀態(tài)('resolved','rejected','pending')的fire相關(guān)方法:

  • --resolve、resolveWith
  • --reject、rejectWith
  • --notify、notifyWith

好了,jQuery.each(tuples, function(i, tuple){…})解讀就到此結(jié)束了。

總結(jié):

通過jQuery.each遍歷tuples,將tuples里的三種狀態(tài)操作值done、fail以及progress添加到promise對象,并分別指向各自自定義對象中的add方法。如果狀態(tài)為resolved或rejected,那么,再將三個特定函數(shù)添加到各自自定義對象的list列表下。隨后,就是對deferred對象賦予三個狀態(tài)各自的觸發(fā)事件啦。

至此,promise、deferred對象如下圖所示:

 

我們在前面講解promise對象時,提到過它的promise屬性,即為擴展promise對象,再回顧下:

 

所以接下來,源代碼中的promise.promise(deferred),即為擴展deferred對象,讓原來只有6個觸發(fā)屬性的deferred,同時擁有了promise對象的全部屬性。

緊接著,func.call(deferred, deferred),即為執(zhí)行參數(shù)func,當然,前提是func有值。值得注意的是,是將deferred作為func的執(zhí)行對象以及執(zhí)行參數(shù)的,這一點在promise.then中體現(xiàn)得淋淋盡致(稍后會細說)。

最后$.Deferred返回構(gòu)建好的deferred對象。

到此,構(gòu)建deferred整體流程走完。

三、細說promise.then

promise.then源碼如下:

promise = {
  then: function( /* fnDone, fnFail, fnProgress */ ) {  
    var fns = arguments;
    return jQuery.Deferred( function( newDefer ) {
      jQuery.each( tuples, function( i, tuple ) {
        var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
        // deferred[ done | fail | progress ] for forwarding actions to newDefer
        deferred[ tuple[ 1 ] ]( function() {
          var returned = fn && fn.apply( this, arguments );
          if ( returned && jQuery.isFunction( returned.promise ) ) {
            returned.promise()
              .progress( newDefer.notify )
              .done( newDefer.resolve )
              .fail( newDefer.reject );
          } else {
            newDefer[ tuple[ 0 ] + "With" ](
              this === promise ? newDefer.promise() : this,
              fn ? [ returned ] : arguments
            );
          }
        } );
      } );
      fns = null;
    } ).promise();
  }
}

精簡promise.then的源碼如下:

promise = {
  then: function( /* fnDone, fnFail, fnProgress */ ) {  
    var fns = arguments;
    return jQuery.Deferred( function( newDefer ) {
          ...
        } ).promise();
  }
}

整體架構(gòu)上,可以清晰的看出,promise.then方法最后通過jQuery.Deferred返回了一個新的受限制的deferred對象,即deferred.promise,正因為這樣,所以執(zhí)行完then方法后,我們是不能通過deferred.pomise手動觸發(fā)resolve、reject或notify的。

接下來,我們再一步一步剖析promise.then源碼。

var fns = arguments不過就是將then方法中的參數(shù)賦予fns,在接下來的jQuery.each里使用。接著,就通過jQuery.Deferred返回了一個構(gòu)建好的deferred對象,但是注意,在jQuery.Deferred里有個參數(shù)—匿名函數(shù),還記得在上一小節(jié)末尾處,我們說過如果jQuery.Deferred里有值,就執(zhí)行它,并將構(gòu)建好的deferred作為執(zhí)行對象和參數(shù)傳入么: 

固,promise.then方法中的newDefer指向通過jQuery.Deferred構(gòu)建好的deferred。

緊接著,jQuery.each(tuples, function(i,tuple){…})處理,重點就是deferred[tuple[1]](function(){…});,注意,這里的deferred是then方法的父deferred哦,如下:

且tuple[1]為—done|fail|progress,在前面我們已經(jīng)談過,它們指向各自自定義事件對象的add方法。因此,也就明白了為什么deferred.resolve|reject|notify后,如果隨后有then,會觸發(fā)then方法的相關(guān)事件,如下:

但是,then方法后有then方法,又是怎么操作的呢?

它會判斷then方法中的回調(diào)函數(shù)的返回值,如果是一個deferred對象,那么就將then方法自行創(chuàng)建的deferred對象中的相關(guān)觸發(fā)事件,添加到回調(diào)函數(shù)中返回的deferred對象的對應(yīng)的list列表中,這樣,當我們觸發(fā)回調(diào)函數(shù)中的相關(guān)觸發(fā)事件后,也就會觸發(fā)then方法的deferred對象了,從而,如果then方法后有then方法,也就關(guān)聯(lián)了。

好了,那么如果then方法中的回調(diào)函數(shù)的返回值是一個非deferred對象呢?那么它就將這個返回值帶上,直接觸發(fā)then方法自行創(chuàng)建的deferred對象的相關(guān)事件,從而,如果then方法后有then方法,也就關(guān)聯(lián)了。

好了,promise.then方法解決就算基本完畢。

四、思考

細細品來,大家有沒有發(fā)現(xiàn),其實promise.then就是通過作用域鏈,利用jQuery.Deferred中的變量deferred來關(guān)聯(lián)父deferred的。如果,你還記得數(shù)據(jù)結(jié)構(gòu)中的單鏈表,有沒有發(fā)覺似曾相識呢,作者在這里通過jQuery.Deferred這個工廠構(gòu)建每個deferred,然后利用作用域鏈相互關(guān)聯(lián),就如同單鏈表一樣。

因此,借助這一思想,我們就一同模擬一個非常簡單的Deferred,稱作SimpleDef。主要作用就是每次我們執(zhí)行SimpleDef函數(shù),它都會返回一個構(gòu)建好的simpleDef對象,該對象里面包含了三個方法done、then以及fire:

  • --done就如同add方法般,將done里的參數(shù)添加到它父simpleDef列表list中,并返回父simpleDef對象;
  • --then就是將其參數(shù)func添加到父SimpleDef對象的列表list中,并返回一個新SimpleDef對象;
  • --fire就是觸發(fā)對應(yīng)simpleDef對象的list列表里的所有函數(shù)。

實現(xiàn)代碼如下:

 function SimpleDef(){
  var list = [],
    simpleDef = {
      done: function(func){
        list.push(func);
        return simpleDef;
      },
      then: function(func){
        list.push(func);
        return SimpleDef();
      },
      fire: function(){
        var i = list.length;
        while(i--){
          list[i]();
        }
      }
    };
  return simpleDef;
}

測試代碼如下: 

var def = SimpleDef();
var then1 = def.done(function(){
  console.log('self1-done1');
}).done(function(){
  console.log('self1-done2');
}).then(function(){
  console.log('self2-then1');
}).done(function(){
  console.log('self2-done1');
});
def.fire();//=>self2-then1 self1-done2 self1-done1
console.log('xxxxxxxxxxxxxxxxxxxx');
then1.fire();//=>self2-done1

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • jquery橫向縱向鼠標滾輪全屏切換

    jquery橫向縱向鼠標滾輪全屏切換

    這篇文章主要為大家詳細介紹了jquery橫向縱向鼠標滾輪全屏切換效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • jQuery的deferred對象使用詳解

    jQuery的deferred對象使用詳解

    jQuery的開發(fā)速度很快,幾乎每半年一個大版本,每兩個月一個小版本。每個版本都會引入一些新功能。今天我想介紹的,就是從jQuery 1.5.0版本開始引入的一個新功能----deferred對象。
    2011-08-08
  • jQuery位置選擇器用法實例分析

    jQuery位置選擇器用法實例分析

    這篇文章主要介紹了jQuery位置選擇器用法,結(jié)合實例形式分析了jQuery位置選擇器的功能、用法及相關(guān)操作注意事項,需要的朋友可以參考下
    2019-06-06
  • jQuery HTML獲取內(nèi)容和屬性操作實例分析

    jQuery HTML獲取內(nèi)容和屬性操作實例分析

    這篇文章主要介紹了jQuery HTML獲取內(nèi)容和屬性操作,結(jié)合實例形式分析了jQuery HTML獲取內(nèi)容和屬性相關(guān)函數(shù)用法與操作注意事項,需要的朋友可以參考下
    2020-05-05
  • jQuery基于ajax實現(xiàn)頁面加載后檢查用戶登錄狀態(tài)的方法

    jQuery基于ajax實現(xiàn)頁面加載后檢查用戶登錄狀態(tài)的方法

    這篇文章主要介紹了jQuery基于ajax實現(xiàn)頁面加載后檢查用戶登錄狀態(tài)的方法,結(jié)合實例形式較為詳細分析了jQuery結(jié)合ajax進行表單登陸驗證操作的具體步驟與相關(guān)操作技巧,需要的朋友可以參考下
    2017-02-02
  • jQuery實現(xiàn)基本淡入淡出效果的方法詳解

    jQuery實現(xiàn)基本淡入淡出效果的方法詳解

    這篇文章主要介紹了jQuery實現(xiàn)基本淡入淡出效果的方法,結(jié)合實例形式詳細分析了jQuery使用fadeIn()、fadeOut()及fadeToggle()等方法控制頁面元素淡入淡出顯示效果的相關(guān)操作技巧,需要的朋友可以參考下
    2018-09-09
  • Jquery實現(xiàn)仿新浪微博獲取文本框能輸入的字數(shù)代碼

    Jquery實現(xiàn)仿新浪微博獲取文本框能輸入的字數(shù)代碼

    Jquery實現(xiàn)仿新浪微博獲取文本框所能輸入的字數(shù),感興趣的朋友可以研究一下代碼方便你折騰,希望本文提供的方法可以幫助到你
    2013-02-02
  • jquery.validate分組驗證代碼

    jquery.validate分組驗證代碼

    在很多時候,我們都不是一步就將所有信息填寫完整,然后提交。而是分步進行填寫表單
    2011-03-03
  • jQuery選擇器用法實例詳解

    jQuery選擇器用法實例詳解

    這篇文章主要介紹了jQuery選擇器用法,結(jié)合實例分析了jQuery選擇器的具體使用技巧與注意事項,需要的朋友可以參考下
    2015-12-12
  • jQuery插件MixItUp實現(xiàn)動畫過濾和排序

    jQuery插件MixItUp實現(xiàn)動畫過濾和排序

    MixItUp過濾排序jQuery插件是一款鼠標滑過圖片顯示描述的jQuery過濾排序插件。是一款輕量,但功能強大的 jQuery 插件,提供了對分類和有序內(nèi)容的美麗的動畫過濾和排序功能。特別適合用于作品集網(wǎng)站,畫廊,圖片博客以及任何的分類或有序內(nèi)容。
    2015-04-04

最新評論