jQuery源码学习(版本1.11)-ready

本文详细分析了jQuery-1.11.1.js源码中ready方法的功能实现,包括其内部机制、参数处理、事件监听与执行流程,帮助开发者理解如何在DOM加载完毕后执行异步回调函数,提高前端开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

本文详细分析了jquery-1.11.1.js源码文件行数:3394~3537;
代码简介:
定义了jQuery.fn.ready,本质是使用Deferred实现的一个执行异步回调函数的机制,在dom加载完毕之后,执行绑定的回调函数;
下文是代码的详细分析。

代码分析

// Deferred对象
var readyList;

// JQ对象的ready方法
// 后续代码可以发现,其实无论哪个JQ对象去执行ready方法,实际效果都是一样的,从这里也可以看出上下文没有应用在jQuery.ready里面
// 实际ready方法只有一个readyList,也只使用一个Deferred
jQuery.fn.ready = function( fn ) {
	// 实际执行就是将传入的fn添加到readyList中
	jQuery.ready.promise().done( fn );

	return this;
};

// 扩展工具变量及函数
jQuery.extend({
	// 用于判断dom节点是否已经加载完毕
	isReady: false,

	// 计数变量,每执行一次holdReady都会加1
	readyWait: 1,

	// hold不为空则增加readyWait,否则触发一次ready
	holdReady: function( hold ) {
		if ( hold ) {
			jQuery.readyWait++;
		} else {
			jQuery.ready( true );
		}
	},

	// 定义工具方法ready
	// 当JQ对象调ready方法时,会执行jQuery.ready.promise,该方法里面实际就是执行本ready方法
	ready: function( wait ) {

		// 如果--jQuery.readyWait不为0则返回,表示还需要等待,需要继续触发ready
		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
			return;
		}

		// 异步递归兼容某些bug防止document.body没加载完
		if ( !document.body ) {
			return setTimeout( jQuery.ready );
		}

		// 设置isReady为true
		jQuery.isReady = true;

		// --jQuery.readyWait > 0则必须继续等待,需要继续触发ready
		if ( wait !== true && --jQuery.readyWait > 0 ) {
			return;
		}

		// 执行readyList里的回调
		readyList.resolveWith( document, [ jQuery ] );

		// 触发ready函数执行绑定的其他回调
		if ( jQuery.fn.triggerHandler ) {
			jQuery( document ).triggerHandler( "ready" );
			jQuery( document ).off( "ready" );
		}
	}
});

/**
 * 去除事件监听
 */
function detach() {
	if ( document.addEventListener ) {
		document.removeEventListener( "DOMContentLoaded", completed, false );
		window.removeEventListener( "load", completed, false );

	} else {
		document.detachEvent( "onreadystatechange", completed );
		window.detachEvent( "onload", completed );
	}
}

/**
 * ready事件回调函数
 */
function completed() {
	// IE也支持document.readyState
	// 这里使用event.type我感到不解,函数没有形参,应该使用arguments去获取事件,这感觉像是代码缺陷
	if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
		// 因为注册了两个监听器,因此先调用detach去除事件监听,以免多次执行回调
		detach();
		// 执行工具函数ready
		jQuery.ready();
	}
}

定义jQuery.ready.promise函数,当JQ对象调用ready函数时,实际就是执行这个函数
jQuery.ready.promise = function( obj ) {
	// 判断readyList是否为空,没有则进入
	if ( !readyList ) {
		// 初始化readyList
		readyList = jQuery.Deferred();

		// 使用document.readyState的值判断是否可触发延迟回调
		if ( document.readyState === "complete" ) {
			// 使用setTimeout制造异步,保证jQuery.ready在当前一段js代码跑完后再执行
			setTimeout( jQuery.ready );

		// document.readyState值不为"complete",则监听事件,
		} else if ( document.addEventListener ) {
			// 监听DOMContentLoaded事件
			document.addEventListener( "DOMContentLoaded", completed, false );

			// 同时监听load事件,是为了保证JQ对象调用ready时,实际页面已经处于完全加载的状态
			// 某些浏览器onload有可能优先执行,就没有执行DOMContentLoaded了
			window.addEventListener( "load", completed, false );

		// 兼容IE
		} else {
			document.attachEvent( "onreadystatechange", completed );

			window.attachEvent( "onload", completed );

			// If IE and not a frame
			// continually check to see if the document is ready
			var top = false;

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

			if ( top && top.doScroll ) {
				(function doScrollCheck() {
					if ( !jQuery.isReady ) {

						try {
							// Use the trick by Diego Perini
							// http://javascript.nwbox.com/IEContentLoaded/
							top.doScroll("left");
						} catch(e) {
							return setTimeout( doScrollCheck, 50 );
						}

						// detach all dom ready events
						detach();

						// and execute any waiting functions
						jQuery.ready();
					}
				})();
			}
		}
	}
	// 返回Deferred里的promise对象
	return readyList.promise( obj );
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值