jQuery Sizzle 入口 [ 源码分析 ]

var Sizzle = function( selector, context, results, seed ) {
	//context 默认为document,可以人为指定
	results = results || [];
	context = context || document;

	var origContext = context;
	//判断文档节点
	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
		return [];
	}
	//表达式是否为字符串
	if ( !selector || typeof selector !== "string" ) {
		return results;
	}
	//set: 种子集
	//checkSet: 种子集的副本
	//extra: 保存剩余的并联表达式,注意是并联表达式
	//ret: 初步返回的结果,数据类型为json,包含set与expr属性,set为种子集,expr为剩余的块表达式
	//cur: 块表达式关系符如:+ > ~ ,如果没有则默认为空格 " "
	//pop:弹出数组的最后一个变量
	//parts: 存储当前被分割的块表达式数组
	var m, set, checkSet, extra, ret, cur, pop, i,
		prune = true,
		contextXML = Sizzle.isXML( context ),
		parts = [],
		soFar = selector;

	// Reset the position of the chunker regexp (start from head)
	// 分割快表达式,对于碰到并联表达式,则暂时结束,将余下的并联表达式保存在extra中
	// 如#info .p,div.red > a
	do {
		chunker.exec( "" );
		m = chunker.exec( soFar );

		if ( m ) {
			soFar = m[3];

			parts.push( m[1] );
			//根据上面的chunker正则,如果存在并联表达式,那么exec之后,m[2]的值为 ","
			if ( m[2] ) {
				//m[3]为剩余的并联表达式
				extra = m[3];
				break;
			}
		}
	} while ( m );
	//会被切割为如下:
	//parts ["#info",".p"] ,extra : div.red > a
	//判断是否存在位置伪类 origPOS,如 :frist,:last 等,如果存在位置伪类,则采取自左向右搜索
	if ( parts.length > 1 && origPOS.exec( selector ) ) {

		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context, seed );

		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] ) {
					selector += parts.shift();
				}

				set = posProcess( selector, set, seed );
			}
		}

	} else {
		// Take a shortcut and set the context if the root selector is an ID
		// (but not if it'll be faster if the inner selector is an ID)
		// 这里主要修改context,如果表达式的开头为ID类型,且最后一个表达式非ID类型
		// 所有的查询,使用到sizzle.selector.find的,只有最后一个节点,其它的节点都可以采用关系来决定
		// 之所以只有第一个为id的时候可以修改context,且最后一个不能为ID,因为getElementById只在document中存在
		// 在element中不存在该方法,如最后一个元素为ID,那么直接就会报错
		// 那么将context修改为ID所在节点,用来提高效率
		// 如:$("#info .p"); 那么自动修改为如下: $(".p",$("#info"));
		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
			//调用find方法,查询parts中的第一个元素,返回临时结果
			//如以上的#info .p 那么会直接查询#info ,返回结果
			ret = Sizzle.find( parts.shift(), context, contextXML );
			//然后修改context
			context = ret.expr ?
				Sizzle.filter( ret.expr, ret.set )[0] :
				ret.set[0];
		}

		if ( context ) {
			//因为是采用自右向左的方式搜索,那么先获取出数组的最后一个元素,调用parts.pop();
			//再调用find方法查询
			ret = seed ?
				{ expr: parts.pop(), set: makeArray(seed) } :
				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
			//对结果进行过滤
			set = ret.expr ?
				Sizzle.filter( ret.expr, ret.set ) :
				ret.set;

			if ( parts.length > 0 ) {
				checkSet = makeArray( set );

			} else {
				prune = false;
			}
			//继续操作块表达式中最后一个元素左边的部分,如果存在,调用关系正则判断
			//如 1 .div.red > p,那么上面已经查询过p了,那么现在parts中的元素为['div.red','>'];
			//   2 .div.red p,那么上面已经查询过p了,那么现在parts中的元素为['div.red'];
			while ( parts.length ) {
				//取出最后一个元素,即为'>' ,如果为第二种情况,那么直接就是 'div.red'
				cur = parts.pop();
				pop = cur;
				//是否存在关系符号,如果为空格
				if ( !Expr.relative[ cur ] ) {
					//查询是否存在cur类型,如为第二种情况,那么给cur赋值为"",这时的pop 则为div.red
					cur = "";
				} else {
					//取出上一级元素,如为第一种情况,需要重新取最后一个元素,为div.red
					pop = parts.pop();
				}
				//如果pop为空,那么默认为document
				if ( pop == null ) {
					pop = context;
				}
				//调用关系正则判断,pop为左边元素的表达式,checkSet为待过滤元素,cur 为关系符
				//这里要强调的一点是,可以有人会发现,这里是直接调用,没有返回值,
				//那么过滤之后的得到的结果怎么返回呢?
				//这里是采用了javascript函数对数组的传递是传值的方式,调用的函数中修改checkSet的值
				//那么在后面去使用checkSet的值,也会变化
				Expr.relative[ cur ]( checkSet, pop, contextXML );
			}

		} else {
			checkSet = parts = [];
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		Sizzle.error( cur || selector );
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );

		} else if ( context && context.nodeType === 1 ) {
			for ( i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}

		} else {
			for ( i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}

	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		//存在并联表达式,递归调用
		Sizzle( extra, origContext, results, seed );
		//对结果排序去重,合并
		Sizzle.uniqueSort( results );
	}

	return results;
};

时间: 2024-10-06 11:16:40

jQuery Sizzle 入口 [ 源码分析 ]的相关文章

jQuery Sizzle.find [ 源码分析 ]

Sizzle.find = function( expr, context, isXML ) { var set, i, len, match, type, left; //expr是否为空 if ( !expr ) { return []; } for ( i = 0, len = Expr.order.length; i < len; i++ ) { type = Expr.order[i]; //根据Expr.order,即Sizzle.selectors.order 的四种类型:ID,C

Jquery之isPlainObject源码分析

今天对Jquery中 isPlainObject 源码分析. 1.  isPlainObject 方法的作用: 用来判断传入参数,是否是对象. 2. 源码分析:isPlainObject: function( obj ) { // 1. jQuery.type( obj ): 通过 juery 封装的类型判断方法,如果不是 Object类型,返回false // 2. obj.nodeType : 用来判断是否是dom 节点, 如果是,返回 false // 3. jQuery.isWindow

Sizzle.filter [ 源码分析 ]

最近一直在研究Sizzle选择器,对于其中的原理确实不得不佩服! Sizzle中的filter方法,主要负责块表达式过滤元素集合,在方法内部会调用Sizzle.selector.fitler方法执行过滤操作. Sizzle.filter主要分5个关键步骤: 1 使用LeftMatch确定表达式类型. 2 调用Sizzle.selectors.preFilter预过虑函数,执行过滤前的修正. 3 调用Sizzle.selectors.filter[ type ] 中对应的过滤函数,执行过滤操作,如

jQuery实现jsonp源码分析(京东2015面试)

// Bind script tag hack transportjQuery.ajaxTransport( "script", function(s) { // This transport only deals with cross domain requests if ( s.crossDomain ) { var script, head = document.head || jQuery("head")[0] || document.documentEle

jquery ui widget 源码分析

jquery ui 的所有组件都是基于一个简单,可重用的widget. 这个widget是jquery ui的核心部分,实用它能实现一致的API,创建有状态的插件,而无需关心插件的内部转换. $.widget( name, base, prototype ) widget一共有2或3个参数.base为可选. 这里之所以把base放在第二个参数里,主要是因为这样写代码更直观一些.(因为后面的prototype 是个代码非常长的大对象). name:第一个参数是一个包含一个命名空间和组件名称的字符串

jQuery Event add [ 源码分析 ]

/* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { add: function( elem, types, handler, data, selector ) { var elemData, eventHandle, eve

jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

转:http://www.cnblogs.com/aaronjs/p/3440647.html?winzoom=1 事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析实现的, 所以需要有一定的基础才行 为了下一步更好的理解内部的实现,所以首先得清楚的认识到事件接口的划分 网上资料遍地都是,但是作为一个jQuery系列的源码分析,我还是很有必要在重新

jQuery1.9.1源码分析--数据缓存Data模块

阅读目录 jQuery API中Data的基本使用方法介绍 jQuery.acceptData(elem)源码分析 jQuery.data(elem, name, data)源码分析 internalRemoveData方法源码分析 internalData方法的源码分析 jQuery.fn.extend({data: function( key, value ) {}})源码分析 jQuery.extend({removeData: function( elem, name ) {}})源码分

jquery源码分析(二)——结构

再来复习下整体架构: jQuery源码分析(基于 jQuery 1.11 版本,共计8829行源码) (21,94)                定义了一些变量和函数jQuery=function(){} (96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展