本文章爲 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);
通常狀況下,root
是 jQuery(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); }