jQuery源码学习(版本1.11)-创建jQuery对象

13 篇文章 0 订阅

创建jQuery对象的整体架构

源码架构分解如下:

// 整个架构就是闭包自执行,参数factory是一个函数,闭包最终其实也就是执行factory
(function( global, factory ) {
	if ( typeof module === "object" && typeof module.exports === "object" ) {
		module.exports = global.document ?
			factory( global, true ) :
			function( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			};
	} else {
		factory( global );
	}

}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

var
	version = "1.11.1",

        // jQuery对象构造函数
	jQuery = function( selector, context ) {
		return new jQuery.fn.init( selector, context );
	};

	// jQuery对象原型
	jQuery.fn = jQuery.prototype = {
		jquery: version,

		constructor: jQuery,

		selector: "",

		length: 0,

		// 省略在原型定义的公用函数
		
	};
	
// line 2747
<span style="white-space:pre">	</span>// 定义jQuery.fn.init
	init = jQuery.fn.init = function( selector, context ) {
	// 省略
	};
	
line 2853
// 原型指向jQuery原型
init.prototype = jQuery.fn;

// line 10299
// 执行完毕向window导出jQuery和$	
if ( typeof noGlobal === strundefined ) {
	window.jQuery = window.$ = jQuery;
}

return jQuery;

}));
 

从上架构简易分解可以看出,当使用$(selector)或者jQuery(selector)等方式创建jQuery对象时,执行的是如下函数:

jQuery = function( selector, context ) {
	return new jQuery.fn.init( selector, context );
};

即实际调用的是jQuery.fn.init( selector, context )去得到最终对象。

仔细分析发现我们在是创建jQuery对象的时候并没有使用关键字new,但创建出来的对象实例依然跟jQuery.prototype关联起来了,拥有其所有属性,这是因为执行jQuery()时,返回结果是new jQuery.fn.init(selector, context),也就是实际jQuery对象是通过init函数构造出来的,最后通过init.prototype = jQuery.fn使得其与jQuery函数的原型关联起来。


jQuery.fn.init详细分析

从源码2733行开始,详细分析如下:

// jQuery(document)
var rootjQuery,

	document = window.document,
	
	// 校验HTML格式,优先匹配id防止XSS通过location.hash注入
	// (?:exp)表示不捕获匹配文本不给分组记号,因此本正则第一个分组(<[\w\W]+>)匹配html标签,之后[^>]*匹配右非尖括号的任意字符,第二个分组([\w-]*)匹配id
	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,

	// context是限定上下文(即确定范围),不传则默认是document,即在document下创建JQ对象
	init = jQuery.fn.init = function( selector, context ) {
		var match, elem;

		// 若selector为各种空则返回空对象
		if ( !selector ) {
			return this;
		}

		// 对selector值不同情况进行分类处理
		// 处理selector是字符串的场景
		if ( typeof selector === "string" ) {
			// 寻找html标签,则将其放在match[1]位置
			if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
				// 原注释意思是有rquickExpr匹配不到html标签的场景(我暂时想不出哪种场景会这样),因此需要这个if判断
				match = [ null, selector, null ];

			} else {
				match = rquickExpr.exec( selector );
			}

			// 有html的情况,以及单独id没有context的情况
			if ( match && (match[1] || !context) ) {

				// 传入html的场景,HANDLE: $(html) -> $(array)
				if ( match[1] ) {
					// 获取原生上下文,防止传入的是JQ对象
					context = context instanceof jQuery ? context[0] : context;

					// jQuery.parseHTML()将传入html转成dom数组与this合并
					jQuery.merge( this, jQuery.parseHTML(
						match[1],
						context && context.nodeType ? context.ownerDocument || context : document,
						true
					) );

					// 传入html及其属性的场景,HANDLE: $(html, props)
					// rsingleTag保证match[1]必须是单标签
					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
						// 遍历context属性
						for ( match in context ) {
							// 如果属性名是jQuery内部方法,则执行且属性值作为参数传入
							if ( jQuery.isFunction( this[ match ] ) ) {
								this[ match ]( context[ match ] );

							// 否则调用attr设置进标签中,前面jQuery.parseHTML()已经根据html生成dom对象了
							} else {
								this.attr( match, context[ match ] );
							}
						}
					}

					return this;

				// 调用document.getElementById()找match[2]匹配的id对应的dom对象
				} else {
					elem = document.getElementById( match[2] );

					// 规避Blackberry 4.6会找到已经不存在的节点的bug,因此要判断parentNode是否存在(bug id #6963)
					if ( elem && elem.parentNode ) {
						// 防止IE跟Opera的document.getElementById()返回的是以name获取的dom对象
						if ( elem.id !== match[2] ) {
							// 调用find即调用Sizzle去查找节点
							return rootjQuery.find( selector );
						}

						// 将得到的dom对象放进JQ对象(类数组结构)
						this.length = 1;
						this[0] = elem;
					}
					
					// 最后定义JQ对象的context跟selector属性,然后返回
					this.context = document;
					this.selector = selector;
					return this;
				}

			// HANDLE: $(expr, $(...)),处理context不存在或者是一个JQ对象(通过context.jquery这个版本属性来判断)的场景,均是直接调用find()
			} else if ( !context || context.jquery ) {
				return ( context || rootjQuery ).find( selector );

			// HANDLE: $(expr, context),处理context不是JQ对象的场景
			// 使用this.constructor( context )生成JQ对象,然后调用find()
			} else {
				return this.constructor( context ).find( selector );
			}

		// 处理selector是dom对象的场景
		// 直接将其引用传入JQ对象以及this.context中,设置一下length,然后返回(备注:window对象没有nodeType)
		} else if ( selector.nodeType ) {
			this.context = this[0] = selector;
			this.length = 1;
			return this;

		// 处理selector是function的场景
		// 调用ready的简便写法:$(function(){})
		} else if ( jQuery.isFunction( selector ) ) {
			return typeof rootjQuery.ready !== "undefined" ?
				rootjQuery.ready( selector ) :
				// Execute immediately if ready is not present
				selector( jQuery );
		}

		// 以上全部不满足则执行以下代码,传进来的selector如果是一个JQ对象,则会有selector跟context属性,就会进入以下if分支
		// 最后jQuery.makeArray()相当于复制selector这个JQ对象返回(makeArray会把selector里的属性全部浅拷贝进this然后返回this)
		if ( selector.selector !== undefined ) {
			this.selector = selector.selector;
			this.context = selector.context;
		}

		return jQuery.makeArray( selector, this );
	};

// init函数的原型指向jQuery.fn即指向jQuery函原型
init.prototype = jQuery.fn;

// 初始化rootjQuery
rootjQuery = jQuery( document );

总结,创建jQuery对象传入参数的情况如下:

selectorcontextreturn
/空JQ对象
单独标签如<li>,<li></li>,不含text
即$(html, props)
纯粹对象创建dom,执行context,包装返回
html字符串,不满足单独标签/创建dom包装返回
#id空/上下文对象context为空直接调用document.getElementById(id),
存在上下文对象则用find()
复杂选择器/find()
dom节点/包装返回
function/当做参数传入执行ready
JQ对象/赋值selector,contexnt,调用makeArray返回
其他/调用makeArray返回




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值