JQuery选择器代码详解(三)——tokenize方法

原创文章,转载请注明出处,多谢!

/*
 * tokenize函数是选择器解析的核心函数,它将选择器转换成两级数组groups
 * 举例:
 *     若选择器为“div.class,span”,则解析后的结果为:
 *     group[0][0] = {type:'TAG',value:'div',matches:match}
 *     group[0][1] = {type:'CLASS',value:'.class',matches:match}
 *     group[1][0] = {type:'TAG',value:'span',matches:match}
 * 由上述结果可以看出,groups的每一个元素以逗号分隔的选择器块的解析结果,
 * 另外,上述结果中的matches等于模式匹配的结果,由于在此不方便写清楚,
 * 故只把代码matches:match写在这里。
 *
 * @param selector 待解析的选择器字符串
 * @param parseOnly 为true时,说明本次调用是匹配子选择器
 *     举个例子:若初始选择器为"div:not(.class:not(:eq(4))):eq(3)"
 *     代码首先匹配出TAG选择器div,
 *     之后匹配出的pseudo选择器字符串是:not(.class:not(:eq(4))):eq(3),
 *     代码会把“.class:not(:eq(4))):eq(3”作为not的括号内的值进一步进行解析,
 *     此时代码在调用tokenize解析时,parseOnly参数会传入true.
 */
function tokenize(selector, parseOnly) {
	var matched, match, tokens, type, soFar, groups, preFilters,
	// 获取缓存中的结果
	cached = tokenCache[selector + " "];

	/*
	 * 若缓存中有selector对应的解析结果
	 * 则执行if中语句体
	 */
	if (cached) {
		// 若是对初始选择器解析(parseOnly!=true),则返回缓存结果,
		// 若不是,则返回0
		return parseOnly ? 0 : cached.slice(0);
	}

	/*
	 * 由于字符串在javascript中不是作为对象来处理的,
	 * 所以通过赋值,代码就自动复制了一个新字符串给了soFar,
	 * 这样,对soFar的任何处理都不会影响selector的原有数据
	 */
	soFar = selector;
	groups = [];
	// 此处赋值,仅仅用于减少后续代码字数,缩短执行路径
	preFilters = Expr.preFilter;

	while (soFar) {

		// Comma and first run
		/*
		 * rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*")
		 * rcomma用来判定是否存在多个选择器块,即用逗号隔开的多个并列的选择器
		 *
		 * 下面条件判定依次为:
		 * !matched:若是第一次执行循环体,则为true;否则为false。
		 *           这里matched即作为是否第一次执行循环体的标识,
		 *           也作为本次循环中soFar是否以非法字符串(即非合法单一选择器)开头的标志。
		 * (match = rcomma.exec(soFar):获取符合rcomma的匹配项
		 */
		if (!matched || (match = rcomma.exec(soFar))) {
			if (match) {
				// Don't consume trailing commas as valid
				/*
				 * 剔除掉第一个逗号及之前的所有字符
				 * 举个例子:
				 * 若初始选择器为:"div.news,span.closed",
				 * 在解析过程中,首先由后续代码解析完毕div.news,剩下",span.closed"
				 * 在循环体内执行到这里时,将逗号及之前之后连续的空白(match[0])删除掉,
				 * 使soFar变成"span.closed",继续执行解析过程
				 *
				 * 在这里,若初始选择器的最后一个非空白字符是逗号,
				 * 那么执行下面代码时soFar不变,即soFar.slice(match[0].length)返回空字符串,
				 * 故最终返回的是||后面的soFar
				 */
				soFar = soFar.slice(match[0].length) || soFar;
			}

			/*
			 * 在第一次执行循环体或者遇到逗号分割符时,将tokens赋值为一个空数组,
			 * 同时压入groups数组
			 */
			groups.push(tokens = []);
		}

		matched = false;

		// Combinators
		/*
		 * rcombinators = new RegExp(
		 *		"^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"),
		 * rcombinators用来匹配四种关系符,即>+~和空白
		 *
		 * 若soFar中是以关系符开始的,则执行if内的语句体
		 */
		if ((match = rcombinators.exec(soFar))) {
			/*
			 * 将match[0]移除match数组,同时将它赋予matched
			 * 若原本关系符两边带有空格,则此时match[0]与matched是不相等的
			 * 举个例子:
			 * 若soFar = " + .div";
			 * 执行match = rcombinators.exec(soFar)后,
			 * match[0] = " + ",而match[1]="+";
			 * 执行完matched = match.shift()后,
			 * matched=" + ",而match[0]="+";
			 */
			matched = match.shift();
			// 将匹配结果压入tokens数组中
			tokens.push({
				value : matched,
				// Cast descendant combinators to space
				/*
				 * rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)"
				 *			+ whitespace + "+$", "g"),
				 * whitespace = "[\\x20\\t\\r\\n\\f]";
				 *
				 * 下面match[0].replace(rtrim, " ")的作用是将match[0]左右两边的空白替换为空格
				 * 但是由于其上的match.shift的作用,match[0]已经是两边不带空白的字符串了,
				 * 故此出的替换是没有用途的代码
				 */
				type : match[0].replace(rtrim, " ")
			});

			// 将关系符之后的字符串赋予soFar,继续解析
			soFar = soFar.slice(matched.length);
		}

		// Filters
		/*
		 * 下面通过for语句对soFar逐一匹配ID、TAG、CLASS、CHILD、ATTR、PSEUDO类型的选择器
		 * 若匹配到了,则先调用该类型选择器对应的预过滤函数,
		 * 然后,将结果压入tokens数组,继续本次循环。
		 */
		for (type in Expr.filter) {
			/*
			 * match = matchExpr[type].exec(soFar):对soFar调用type类型的正则表达式对soFar进行匹配,
			 *     并将匹配结果赋予match。若未匹配到数据,则match为undefined。
			 * !preFilters[type]:若不存在type类型的预过滤函数,则为true
			 * match = preFilters[type](match):执行预过滤,并将结果返回给match
			 *
			 */
			if ((match = matchExpr[type].exec(soFar))
					&& (!preFilters[type] || (match = preFilters[type]
							(match)))) {
				// 将match[0]移除match数组,同时将它赋予matched
				matched = match.shift();
				// 将匹配结果压入tokens数组中
				tokens.push({
					value : matched,
					type : type,
					matches : match
				});
				// 将匹配结果之后的字符串赋予soFar,继续解析
				soFar = soFar.slice(matched.length);
			}
		}

		/*
		 * 若matched==false,
		 * 则说明本次循环没有有效的选择器(包括关系符和id、class等类型选择器)
		 * 因此,解析到当前位置遗留下来的soFar是非法的选择器字符串
		 * 跳出while循环体
		 */
		if (!matched) {
			break;
		}
	}

	// Return the length of the invalid excess
	// if we're just parsing
	// Otherwise, throw an error or return tokens
	/*
	 * 若不是对初始选择器字符串进行解析(!parseOnly==true),
	 *     则返回soFar.length,此时的soFar.length代表连续有效的选择器最终位置,
	 *     后续文章将以实例进行说明
	 * 若是对初始选择器字符串进行解析,则看soFar是否还有字符,
	 *     若是,则执行Sizzle.error(selector)抛出异常;
	 *     若不是,则执行tokenCache(selector, groups).slice(0)将结果压入缓存,并返回结果的副本。
	 */
	return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) :
	// Cache the tokens
	tokenCache(selector, groups).slice(0);
}

jQuery选择器代码详解(一)——Sizzle方法

jQuery选择器代码详解(二)——select方法

时间: 2024-10-14 04:36:30

JQuery选择器代码详解(三)——tokenize方法的相关文章

jQuery选择器代码详解(五)——实例说明tokenize的解析过程

原创文章,转载请写明出处,多谢! 以下分析基于jQuery-1.10.2.js版本. 下面将以$("div:not(.class:contain('span')):eq(3)")为例,说明tokenize和preFilter各段代码是如何协调完成解析的.若想了解tokenize方法和preFilter类的每行代码的详细解释,请参看如下两篇文章: jQuery选择器代码详解(三)--tokenize方法 jQuery选择器代码详解(四)--Expr.preFilter 下面是tokeni

jQuery选择器代码详解(二)——select方法

原创文章,转载请注明出处,多谢! /* * @param selector 已去掉头尾空白的选择器字符串 * @param context 执行匹配的最初的上下文(即DOM元素集合).若context没有赋值,则取document. * @param results 已匹配出的部分最终结果.若results没有赋值,则赋予空数组. * @param seed 初始集合 */ function select(selector, context, results, seed) { var i, to

jQuery选择器代码详解(四)——Expr.preFilter

原创文章,转载请注明出处,多谢! Expr.preFilter是tokenize方法中对ATTR.CHILD.PSEUDO三种选择器进行预处理的方法.具体如下: Expr.preFilter : { "ATTR" : function(match) { /* * 完成如下任务: * 1.属性名称解码 * 2.属性值解码 * 3.若判断符为~=,则在属性值两边加上空格 * 4.返回最终的mtach对象 * * match[1]表示属性名称, * match[1].replace(rune

jQuery选择器代码详解(七)——elementMatcher函数

要读懂Sizzle的Compile执行过程,首先需要弄清楚涉及的各个子程序的功能和关键变量和作用,我将逐一对jQuery-1.10.2版本的Compile代码进行说明,望能给予大家帮助. elementMatcher(matchers) 1.源码 function elementMatcher(matchers) { return matchers.length > 1 ? function(elem, context, xml) { var i = matchers.length; while

jQuery选择器代码详解(八)——addCombinator函数

function addCombinator(matcher, combinator, base) 1.源码 function addCombinator(matcher, combinator, base) { var dir = combinator.dir, checkNonElements = base && dir === "parentNode", doneName = done++; return combinator.first ? // Check a

jQuery选择器代码详解(一)——Sizzle方法

对jQuery的Sizzle各方法做了深入分析(同时也参考了一些网上资料)后,将结果分享给大家.我将采用连载的方式,对Sizzle使用的一些方法详细解释一下,每篇文章介绍一个方法. 若需要转载,请写明出处,多谢. /* * @param selector 选择器字符串 * @param context 执行匹配的最初的上下文(即DOM元素).若context没有赋值,则取document. * @param results 已匹配出的部分最终结果.若results没有赋值,则赋予空数组. * @

jquery.easyui代码详解,和遇到的问题,提供大家在使用的时候少走弯路(一)

初次使用jquery.easyui这个东东,虽然简单,但还是很费力的去研究了一下使用,在使用过程中遇到的问题,下面代码会详细的注释到 引用的文件jquery.min.js              jquery.easyui.min.js 样式:icon.css         easyui.css 页面初始化easy ui 插件 <html xmlns="http://www.w3.org/1999/xhtml"> <div class="cg-op&qu

jQuery(sel,[context]) jQuery()实例代码详解

概述 这个函数接收一个包含 CSS 选择器的字符串,然后用这个字符串去匹配一组元素. jQuery 的核心功能都是通过这个函数实现的. jQuery中的一切都基于这个函数,或者说都是在以某种方式使用这个函数.这个函数最基本的用法就是向它传递一个表达式(通常由 CSS 选择器组成),然后根据这个表达式来查找所有匹配的元素. 默认情况下, 如果没有指定context参数,$()将在当前的 HTML document中查找 DOM 元素:如果指定了 context 参数,如一个 DOM 元素集或 jQ

Spring4.0MVC学习资料,ApplicationContext中的方法详解(三)

做为java开源的一部分,spring框架一直排在老大的位置.Spring4.0 是 Spring 推出的一个重大版本升级,进一步加强了 Spring 作为 Java 领域第一开源平台的地位.Spring4.0 引入了众多 Java 开发者期盼的新特性,如泛型依赖注入.SpEL.校验及格式化框架.Rest风格的 WEB 编程模型等.这些新功能实用性强.易用性高,可大幅降低 JavaEE 开发的难度,同时有效提升应用开发的优雅性.为了方便开发,Spring的ApplicationContext类,