本文章为 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); }