<b>3.3 設計思路</b>
在正式開始分析sizzle的源碼實作之前,先來讨論和分析下如果要執行一段選擇器表達式,或者說設計一個簡化版的選擇器引擎,需要做些什麼工作。下面以"div.red>p"為例來模拟執行過程,具體來說有從左向右查找和從右向左查找兩種思路:
1)從左向右:先查找"div.red"比對的元素集合,然後查找比對"p"的子元素集合。
2)從右向左:先查找"p"比對的元素集合,然後檢查其中每個元素的父元素是否比對"div.red"。
無論是從左向右還是從右向左,都必須經曆下面3個步驟:
1)首先要能正确地解析出"div.red>p"中的"div.red"、"p"和">",即解析出選擇器表達式中的塊表達式和塊間關系符。這一步是必需的,否則根本無從下手。
2)然後要能正确地找到與"div.red"或"p"比對的元素集合,即查找單個塊表達式的比對元素集合。以"div.red"為例,可以有兩種實作方式:
a.?先查找比對"div"的元素集合,然後從中過濾出比對".red"的元素集合。
b.?先查找比對".red"的元素集合,然後從中過濾出比對"div"的元素集合。
不管采用以上哪種方式,這個過程都可以分解為兩個步驟:第一步用塊表達式的一部分進行查找,第二步用塊表達式的剩餘部分對查找的結果進行過濾。
3)最後來處理"div.red"和"p"之間的關系符">",即處理塊表達式之間的父子關系。在這一步驟中,從左向右和從右向左的處理方式是截然不同的:
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的源碼實作。