欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery選擇器源碼解讀(三):tokenize方法

 更新時間:2015年03月31日 10:15:36   投稿:junjie  
這篇文章主要介紹了jQuery選擇器源碼解讀(三):tokenize方法,本文用詳細(xì)的注釋解讀了tokenize方法的實現(xiàn)源碼,需要的朋友可以參考下
/*
 * tokenize方法是選擇器解析的核心函數(shù),它將選擇器轉(zhuǎn)換成兩級數(shù)組groups
 * 舉例:
 *  若選擇器為“div.class,span”,則解析后的結(jié)果為:
 *  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}
 * 由上述結(jié)果可以看出,groups的每一個元素以逗號分隔的選擇器塊的解析結(jié)果,
 * 另外,上述結(jié)果中的matches等于模式匹配的結(jié)果,由于在此不方便寫清楚,
 * 故只把代碼matches:match寫在這里。
 * 
 * tokenize方法完成如下兩個主要任務(wù):
 * 1、解析選擇器
 * 2、將解析結(jié)果存入緩存中,以備后用
 * 
 * 
 * @param selector 待解析的選擇器字符串
 * @param parseOnly 為true時,說明本次調(diào)用是匹配子選擇器
 *  舉個例子:若初始選擇器為"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的括號內(nèi)的值進(jìn)一步進(jìn)行解析,
 *  此時代碼在調(diào)用tokenize解析時,parseOnly參數(shù)會傳入true.
 */
function tokenize(selector, parseOnly) {
	var matched, match, tokens, type, soFar, groups, preFilters, 
	// 獲取緩存中的結(jié)果
	cached = tokenCache[selector + " "];

	/*
	 * 若緩存中有selector對應(yīng)的解析結(jié)果
	 * 則執(zhí)行if中語句體
	 */
	if (cached) {
		// 若是對初始選擇器解析(parseOnly!=true),則返回緩存結(jié)果,
		// 若不是,則返回0
		return parseOnly ? 0 : cached.slice(0);
	}

	/*
	 * 由于字符串在javascript中不是作為對象來處理的,
	 * 所以通過賦值,代碼就自動復(fù)制了一個新字符串給了soFar,
	 * 這樣,對soFar的任何處理都不會影響selector的原有數(shù)據(jù)
	 */
	soFar = selector;
	groups = [];
	// 此處賦值,僅僅用于減少后續(xù)代碼字?jǐn)?shù),縮短執(zhí)行路徑
	preFilters = Expr.preFilter;

	while (soFar) {

		// Comma and first run
		/*
		 * rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*")
		 * rcomma用來判定是否存在多個選擇器塊,即用逗號隔開的多個并列的選擇器
		 * 
		 * 下面條件判定依次為:
		 * !matched:若是第一次執(zhí)行循環(huán)體,則為true;否則為false。
		 *   這里matched即作為是否第一次執(zhí)行循環(huán)體的標(biāo)識,
		 *   也作為本次循環(huán)中soFar是否以非法字符串(即非合法單一選擇器)開頭的標(biāo)志。
		 * (match = rcomma.exec(soFar):獲取符合rcomma的匹配項
		 */
		if (!matched || (match = rcomma.exec(soFar))) {
			if (match) {
				// Don't consume trailing commas as valid
				/*
				 * 剔除掉第一個逗號及之前的所有字符
				 * 舉個例子:
				 * 若初始選擇器為:"div.news,span.closed",
				 * 在解析過程中,首先由后續(xù)代碼解析完畢div.news,剩下",span.closed"
				 * 在循環(huán)體內(nèi)執(zhí)行到這里時,將逗號及之前之后連續(xù)的空白(match[0])刪除掉,
				 * 使soFar變成"span.closed",繼續(xù)執(zhí)行解析過程
				 * 
				 * 在這里,若初始選擇器的最后一個非空白字符是逗號,
				 * 那么執(zhí)行下面代碼時soFar不變,即soFar.slice(match[0].length)返回空字符串,
				 * 故最終返回的是||后面的soFar
				 */
				soFar = soFar.slice(match[0].length) || soFar;
			}
			
			/*
			 * 在第一次執(zhí)行循環(huán)體或者遇到逗號分割符時,將tokens賦值為一個空數(shù)組,
			 * 同時壓入groups數(shù)組
			 */
			groups.push(tokens = []);
		}

		matched = false;

		// Combinators
		/*
		 * rcombinators = new RegExp(
		 *		"^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"),
		 * rcombinators用來匹配四種關(guān)系符,即>+~和空白
		 * 
		 * 若soFar中是以關(guān)系符開始的,則執(zhí)行if內(nèi)的語句體
		 */
		if ((match = rcombinators.exec(soFar))) {
			/*
			 * 將match[0]移除match數(shù)組,同時將它賦予matched
			 * 若原本關(guān)系符兩邊帶有空格,則此時match[0]與matched是不相等的
			 * 舉個例子:
			 * 若soFar = " + .div";
			 * 執(zhí)行match = rcombinators.exec(soFar)后,
			 * match[0] = " + ",而match[1]="+";
			 * 執(zhí)行完matched = match.shift()后,
			 * matched=" + ",而match[0]="+";
			 */
			matched = match.shift();
			// 將匹配結(jié)果壓入tokens數(shù)組中
			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]已經(jīng)是兩邊不帶空白的字符串了,
				 * 故此出的替換是沒有用途的代碼
				 */
				type : match[0].replace(rtrim, " ")
			});
			
			// 將關(guān)系符之后的字符串賦予soFar,繼續(xù)解析
			soFar = soFar.slice(matched.length);
		}

		// Filters
		/*
		 * 下面通過for語句對soFar逐一匹配ID、TAG、CLASS、CHILD、ATTR、PSEUDO類型的選擇器
		 * 若匹配到了,則先調(diào)用該類型選擇器對應(yīng)的預(yù)過濾函數(shù),
		 * 然后,將結(jié)果壓入tokens數(shù)組,繼續(xù)本次循環(huán)。
		 */
		for (type in Expr.filter) {
			/*
			 * match = matchExpr[type].exec(soFar):對soFar調(diào)用type類型的正則表達(dá)式對soFar進(jìn)行匹配,
			 *  并將匹配結(jié)果賦予match。若未匹配到數(shù)據(jù),則match為undefined。
			 * !preFilters[type]:若不存在type類型的預(yù)過濾函數(shù),則為true
			 * match = preFilters[type](match):執(zhí)行預(yù)過濾,并將結(jié)果返回給match
			 * 
			 */
			if ((match = matchExpr[type].exec(soFar))
					&& (!preFilters[type] || (match = preFilters[type]
							(match)))) {
				// 將match[0]移除match數(shù)組,同時將它賦予matched
				matched = match.shift();
				// 將匹配結(jié)果壓入tokens數(shù)組中
				tokens.push({
					value : matched,
					type : type,
					matches : match
				});
				// 將匹配結(jié)果之后的字符串賦予soFar,繼續(xù)解析
				soFar = soFar.slice(matched.length);
			}
		}

		/*
		 * 若matched==false,
		 * 則說明本次循環(huán)沒有有效的選擇器(包括關(guān)系符和id、class等類型選擇器)
		 * 因此,解析到當(dāng)前位置遺留下來的soFar是非法的選擇器字符串
		 * 跳出while循環(huán)體
		 */
		if (!matched) {
			break;
		}
	}

	// Return the length of the invalid excess
	// if we're just parsing
	// Otherwise, throw an error or return tokens
	/*
	 * 若不是對初始選擇器字符串進(jìn)行解析(!parseOnly==true),
	 *  則返回soFar.length,此時的soFar.length代表連續(xù)有效的選擇器最終位置,
	 *  后續(xù)文章將以實例進(jìn)行說明
	 * 若是對初始選擇器字符串進(jìn)行解析,則看soFar是否還有字符,
	 *  若是,則執(zhí)行Sizzle.error(selector)拋出異常;
	 *  若不是,則執(zhí)行tokenCache(selector, groups).slice(0)將結(jié)果壓入緩存,并返回結(jié)果的副本。
	 */
	return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) :
	// Cache the tokens
	tokenCache(selector, groups).slice(0);
}

相關(guān)文章

最新評論