天天看點

jQuery技術内幕:深入解析jQuery架構設計與實作原理. 3.3 設計思路

<b>3.3 設計思路</b>

在正式開始分析sizzle的源碼實作之前,先來讨論和分析下如果要執行一段選擇器表達式,或者說設計一個簡化版的選擇器引擎,需要做些什麼工作。下面以"div.red&gt;p"為例來模拟執行過程,具體來說有從左向右查找和從右向左查找兩種思路:

1)從左向右:先查找"div.red"比對的元素集合,然後查找比對"p"的子元素集合。

2)從右向左:先查找"p"比對的元素集合,然後檢查其中每個元素的父元素是否比對"div.red"。

無論是從左向右還是從右向左,都必須經曆下面3個步驟:

1)首先要能正确地解析出"div.red&gt;p"中的"div.red"、"p"和"&gt;",即解析出選擇器表達式中的塊表達式和塊間關系符。這一步是必需的,否則根本無從下手。

2)然後要能正确地找到與"div.red"或"p"比對的元素集合,即查找單個塊表達式的比對元素集合。以"div.red"為例,可以有兩種實作方式:

a.?先查找比對"div"的元素集合,然後從中過濾出比對".red"的元素集合。

b.?先查找比對".red"的元素集合,然後從中過濾出比對"div"的元素集合。

不管采用以上哪種方式,這個過程都可以分解為兩個步驟:第一步用塊表達式的一部分進行查找,第二步用塊表達式的剩餘部分對查找的結果進行過濾。

3)最後來處理"div.red"和"p"之間的關系符"&gt;",即處理塊表達式之間的父子關系。在這一步驟中,從左向右和從右向左的處理方式是截然不同的:

a.?從左向右:找到"div.red"比對的元素集合的子元素集合,然後從中過濾出比對"p"的子元素集合。

b.?從右向左:檢查每個比對"p"的元素的父元素是否比對"div.red",隻保留比對的元素。

無論采用以上哪種方式,這個過程都可以分解為兩個步驟:第一步按照塊間關系符查找元素,第二步用塊表達式對查找的結果進行過濾。不論元素之間是哪種關系(父子關系、祖先後代關系、相鄰的兄弟關系或不相鄰的兄弟關系),都可以采用這種方式來查找和過濾。

另外,如果還有更多的塊表達式,則重複執行第3步。

對于前面的3個步驟,可以進一步提煉總結,如下:

1)處理選擇器表達式:解析選擇器表達式中的塊表達式和塊間關系符。

2)處理塊表達式:用塊表達式的一部分查找,用剩餘部分對查找結果進行過濾。

3)處理塊間關系符:按照塊間關系符查找,用塊表達式對查找結果進行過濾。

從前面對選擇器表達式的執行過程的分析,還可以推導分析出以下結論:

從左向右的總體思路是不斷縮小上下文,即不斷縮小查找範圍。

從右向左的總體思路是先查找後過濾。

在從左向右的查找過程中,每次處理塊間關系符時都需要處理未知數量的子元素或後代元素,而在從右向左的查找過程中,處理塊間關系符時隻需要處理單個父元素或有限數量的祖先元素。是以,在大多數情況下,采用從右向左的查找方式其效果要高于從左向右。

在了解了兩種執行思路後,現在再來看看sizzle,它是一款從右向左查找的選擇器引擎,提供了與前面3個步驟相對應的核心接口:

正則chunker負責從選擇器表達式中提取塊表達式和塊間關系符。

方法sizzle.find( expr, context, isxml )負責查找塊表達式比對的元素集合,方法sizzle.filter(

expr, set, inplace, not )負責用塊表達式過濾元素集合。

對象sizzle.selector.relative中的塊間關系過濾函數根據塊間關系符過濾元素集合。

函數sizzle( selector, context, results, seed )則按照前面3個步驟将這些核心接口組織起來。

本節對選擇器引擎和sizzle的設計思路作了探索和概述,接下來看看sizzle的源碼實作。

繼續閱讀