jQuery 源碼分析:當 selector 傳來一個函數時,怎麼進行處理?

本文章爲 0.9 版本,將會在稍後潤色更新。本文使用的 jQuery 版本爲 3.4.0數組

咱們知道使用 $ 操做符時,能夠往裏面塞不少類型的參數,字符串,對象,函數...,jQuery 會根據不一樣的參數類型,讓咱們執行不一樣的操做。這其實就是「函數重載」的價值所在:它暴露出一個簡潔的接口給用戶,容許用戶在使用這個接口時,經過參數類型控制函數的行爲方式,是一種對用戶很是友好的設計。app

那麼 jQuery 在 $ 這裏的函數重載是怎樣實現的呢?這篇文章咱們只關心其中的一個細枝末節,傳入一個函數時,jQuery 會怎麼作:async

首先咱們看這裏,jQuery 首先會判斷傳入的 selector 是否是一個函數,這裏使用的是包裝後的 isFunction 函數,它基本等同於 typeof 判斷:函數

if (isFunction(selector)) {
  return root.ready !== undefined ?
    root.ready(selector) :

    // Execute immediately if ready is not present
    selector(jQuery);
}

若是判斷是一個函數,jQuery 會去判斷 root.ready 的值,這裏 root 是什麼呢?看下面的代碼:this

// Method init() accepts an alternate rootjQuery
// so migrate can support jQuery.sub (gh-2101)
root = root || rootjQuery;

rootjQuery = jQuery(document);

通常狀況下,rootjQuery(document) 的返回值,這裏就有點繞了,由於咱們原本是要解決「選擇器是函數的時候,jQuery 會怎麼作」的問題,如今咱們先要解決「選擇器是對象的時候,jQuery 會怎麼作」。在源碼裏 jQuery 是這樣處理的:設計

return jQuery.makeArray(selector, this);

咱們愈來愈深刻了,如今咱們要解決 jQuery.makeArray 這個方法在作什麼的問題,這部分的代碼在這裏:code

makeArray: function (arr, results) {
    var ret = results || [];

    if (arr != null) {
      if (isArrayLike(Object(arr))) {
        jQuery.merge(ret,
          typeof arr === "string" ?
            [arr] : arr
        );
      } else {
        push.call(ret, arr);
      }
    }

    return ret;
  },

能夠簡單理解爲,jQuery 會把一個對象傳入到它的數組中。orm

因此到目前爲止,咱們大概弄懂了 root 究竟是個什麼東西,簡單來講,是一個數組,而且第一個元素是 document 對象。咱們繼續,接下來,咱們想知道的其實是 root.ready 是什麼,當咱們回顧一下最初的代碼就能知道,jQuery 處理函數的邏輯就是判斷 root.ready 的值,若是該值爲真值,就調用 root.ready 方法,並把咱們的函數當作參數傳進去,若是爲假值,則直接調用這個函數,把咱們的 jQuery 對象當作參數傳進去。對象

對不起,接下來的重頭戲我將會在往後補上了,此次我先描述一個大概,讓咱們看看和 root.ready 有關的源碼:接口

// The deferred used on DOM ready
var readyList = jQuery.Deferred();

jQuery.fn.ready = function (fn) {

  readyList
    .then(fn)

    // Wrap jQuery.readyException in a function so that the lookup
    // happens at the time of error handling instead of callback
    // registration.
    .catch(function (error) {
      jQuery.readyException(error);
    });

  return this;
};

jQuery.extend({

  // Is the DOM ready to be used? Set to true once it occurs.
  isReady: false,

  // A counter to track how many items to wait for before
  // the ready event fires. See #6781
  readyWait: 1,

  // Handle when the DOM is ready
  ready: function (wait) {

    // Abort if there are pending holds or we're already ready
    if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
      return;
    }

    // Remember that the DOM is ready
    jQuery.isReady = true;

    // If a normal DOM Ready event fired, decrement, and wait if need be
    if (wait !== true && --jQuery.readyWait > 0) {
      return;
    }

    // If there are functions bound, to execute
    readyList.resolveWith(document, [jQuery]);
  }
});

// ===> readyList
var readyList = jQuery.Deferred();

其實註釋裏也寫的很清晰了,jQuery.ready 將會在 DOM 加載完畢後,調用傳入的參數,可是就是這樣一個簡單地功能,jQuery 採用了 jQuery.Deferred() 方法去實現,這個方法到底作了什麼呢?請看我(可能的)下一篇文章:)

總結一下,若是給 jQuery 傳入一個函數類型的參數會發生什麼?不徹底準確的回答是,它會等 DOM 加載完畢後被調用。具體來講,是等待 doument 對象上的 DOMContentLoaded 事件被觸發。

下面是無聲的證據:

jQuery.ready.then = readyList.then;

    // The ready event handler and self cleanup method
    function completed() {
        document.removeEventListener("DOMContentLoaded", completed);
        window.removeEventListener("load", completed);
        jQuery.ready();
    }

    // Catch cases where $(document).ready() is called
    // after the browser event has already occurred.
    // Support: IE <=9 - 10 only
    // Older IE sometimes signals "interactive" too soon
    if (document.readyState === "complete" ||
        (document.readyState !== "loading" && !document.documentElement.doScroll)) {

        // Handle it asynchronously to allow scripts the opportunity to delay ready
        window.setTimeout(jQuery.ready);

    } else {

        // Use the handy event callback
        document.addEventListener("DOMContentLoaded", completed);

        // A fallback to window.onload, that will always work
        window.addEventListener("load", completed);
    }
相關文章
相關標籤/搜索