jQuery文档初始化函数原理

时间:2022-06-24 19:48:27

在jQuery的脚本使用中,经常注册文档初始化函数,比如:$(function(){}); 那么这句话原理是什么呢?

原理一、把completed函数注册到文档加载的原生事件中

关键在这一句document.addEventListener("DOMContentLoaded", completed, false);

jQuery.ready.promise = function (obj) {
if (!readyList) {

// 如果没有,新建一个 Deferred 对象
// Deferred 用于处理异步延时回调函数,也就是内部用于 ready 的一个异步队列
readyList = jQuery.Deferred();

// Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
if (document.readyState === "complete") {
// Handle it asynchronously to allow scripts the opportunity to delay ready
// setTimeout : 在setTimeout中触发的函数, 一定会在DOM准备完毕后触发.(即是 DOMContentLoaded)
setTimeout(jQuery.ready);

// Standards-based browsers support DOMContentLoaded
// 支持 DOMContentLoaded 的浏览器 (除去ie 6 7 8)
} else if (document.addEventListener) {
// Use the handy event callback
// 当检测的 document.readyState 的值不为 complete 时, 用 readystatechange 监听 document.readyState 值的变化事件
document.addEventListener("DOMContentLoaded", completed, false);

// A fallback to window.onload, that will always work
// 一种退而求其次的方法,确保一定会发生
window.addEventListener("load", completed, false);

// If IE event model is used
// 如果是 IE 浏览器(6、7、8)
} else {
// Ensure firing before onload, maybe late but safe also for iframes
document.attachEvent("onreadystatechange", completed);

// A fallback to window.onload, that will always work
window.attachEvent("onload", completed);

// If IE and not a frame
// continually check to see if the document is ready
// 如果是 IE 且不是在 frame 中
var top = false;

try {
top = window.frameElement == null && document.documentElement;
} catch (e) { }

// 如果是IE并且不是iframe
if (top && top.doScroll) {
// 这里有个立即执行函数 doScrollCheck()
(function doScrollCheck() {
if (!jQuery.isReady) {

try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
// Diego Perini 在 2007 年的时候,报告了一种检测 IE 是否加载完成的方式,使用 doScroll 方法调用
// 原理就是对于 IE 在非 iframe 内时,只有不断地通过能否执行 doScroll 判断 DOM 是否加载完毕
// 在上述中间隔 50 毫秒尝试去执行 doScroll,注意,由于页面没有加载完成的时候,调用 doScroll 会导致异常,所以使用了 try - catch 来捕获异常
// 直到DOM渲染结束了,这个时候 doScroll 方法不会抛出异常,然后就调用$.ready()
top.doScroll("left");
} catch (e) {
return setTimeout(doScrollCheck, 50);
}

// detach all dom ready events
detach();

// and execute any waiting functions
jQuery.ready();
}
})();
}
}
}
// 函数返回的是deferred对象,这就可以加上链式操作了
// 可以使用 .done .fail 等方法
return readyList.promise(obj);

// Populate the class2type map
};


原理二、使用deferred对象管理队列方法

还是上面的代码,,注意到有这么一句:readyList = jQuery.Deferred();,这个readyList是闭包内的“全局变量”,在completed调用栈中也会使用到

原理三、completed函数触发后会触发readList的resolveWith方法进行触发

关键代码:readyList.resolveWith(document,[jQuery])进行触发deferred函数队列




/*注册文档就绪函数:$(function(){});*/
/*
首先下面几种写法都是一样的,最终都是jQuery(document).ready(function(){});
$("#box").ready(function(){});
$(function(){});

jQuery(document).ready(fn);
-->jQuery.ready.promise().done(fn);
-->在promise方法中初始化readyList这是一个deferred对象
添加dom加载完成事件"completed"
-->执行readList.promise()的done方法注册dom加载完成方法

当文档加载完成后触发了complete方法,这个方法中执行如下:
-->移除dom加载事件中注册的complete方法
-->执行jQuery.ready()方法
-->调用readyList.resolveWith(document,[jQuery])进行触发deferred函数队列

*/