作者:nuysoft/高雲 QQ:47214707 EMail:nuysoft@gmail.com
聲明:本文為原創文章,如需轉載,請註明來源並保留原文連結。
在分析Sizzle源碼之前,先整理一下選取器的工作原理
先明確一些選取器中用到的名詞,後邊閱讀時不會有歧義:
選取器運算式: "div > p"
塊運算式: "div" "p"
並欄選取器運算式: "div, p"
塊分割器: Sizzle中的chunker正則,對選取器運算式從左向右分割出一個個塊運算式
尋找器: 對塊運算式進行尋找,找到的DOM元素數組叫候選集
過濾器: 對塊運算式和候選集進行過濾
關係過濾器 對塊運算式之間的關係進行過濾,共有四種關係:"+" 緊挨著的兄弟關係;">" 父子關係;"" 祖先關係;"~" 之後的所有兄弟關係
候選集: 尋找器的結果,待過濾器進行過濾
映射集: 候選集的副本,過濾器和關係過濾器對映射集進行過濾
工作流程:
1. 使用塊分割器對選取器運算式進行分割,從左向右
如果遇到用逗號","分割的並欄選取器運算式,只分割至第一個逗號前邊的選取器運算式1,將剩餘部分記錄下來
2. 對最後一個塊運算式進行尋找Sizzle.find,結果放入候選集set,並將塊運算式中匹配的字串部分刪除
尋找器Sizzle.find從正則集Expr.match擷取對應的Regex,對塊運算式進行匹配,匹配成功則從尋找函數集Expr.find擷取對應的尋找函數執行
尋找順序定義在Expr.order中,依次是:ID CLASS NAME TAG,尋找時CLASS需要瀏覽器支援getElementsByClassName
Expr.match中設定了ID CLASS NAME ATTR TAG CHILD POS PSEUDO的正則匹配運算式
3. 如果最後一個塊運算式不為空白(字串),過濾器Sizzle.filter對set進行過濾
過濾器Sizzle.filter僅對單個塊運算式起作用,僅對候選集set中的元素起作用,檢查候選集set中的元素滿足剩餘的塊運算式
在過濾器Sizzle.filter的過濾過程中,不合格被設定為false,合格不做修改
過濾時從正則集Expr.leftMatch擷取對應的Regex,對塊運算式進行匹配,匹配成功則從Expr.filter擷取對應的過濾函數執行
Expr.leftMatch定義了與Expr.match同樣數量的Regex:ID CLASS NAME ATTR TAG CHILD POS PSEUDO
過濾函數集Expr.filter定義了PSEUDO CHILD ID TAG CLASS ATTR POS的過濾函數
過濾器Sizzle.filter進行過濾之前,會先調用預過濾器Expr.preFilter對過濾所需的參數進行修正,但是CLASS是個例外
在CLASS進行預過濾時做了最佳化,直接將匹配class的元素作為候選集返回,縮小過濾範圍,縮小候選集範圍
將以上尋找和過濾得到候選集set複製,放入映射集checkSet,後邊的過濾操作在checkSet上進行
對最後一個塊運算式的尋找和過濾到這裡結束,得到一個候選集set和映射集checkSet
4. 在映射集checkSet上將剩餘的塊運算式從右向左進行過濾,根據與前一個塊運算式的關係,從關係過濾器集Expr.relative中擷取對應的函數執行關係過濾
在關係過濾器Expr.relative的過濾過程中,不合格被設定為false,合格則被設定為父元素、祖先元素、兄長元素
元素之間的關係共有四種:"+" 緊挨著的兄弟關係;">" 父子關係;"" 祖先關係;"~" 之後的所有兄弟關係
在關係過濾器Expr.relative的過濾過程中,如果遇到塊運算式是標籤TAG的情況,則直接比較標籤類型nodeName是否相等
如果不是標籤TAG的情況,則會調用過濾器Sizzle.filter進行過濾,過濾過程見第3步
從右向左過濾,直到所有塊運算式全部過濾完
5. 根據過濾後的映射集checkSet,從候選集set中挑選最終的結果集,在映射集checkSet中
如果是null、false,將被過濾
如果不是Element(nodeType===1),將被過濾
如果上下文不是Document而是某個Element,不是Element的子項目的,將被過濾
6. 如果存在並列運算式,重複1~5,並將得到的最終結果集合并、排序、去重
如果僅有一個選取器運算式,沒有並欄選取器運算式,不需要排序
以下過程不屬於Sizzle,屬於jQuery對Sizzle的擴充
7. 如果存在多個上下文,對每個上下文重複1~6
多個上下文例子:$('div').find('div > p'),$('div')可能找到多個div
其實第7步是jQuery選取器的入口,從第7步去調用1~6,調用時傳入一個空的jQuery對象作為結果集
預設以document為上下文:(context || rootjQuery).find( selector )
8. 將從多個上下文找到的結果集合并、去重,返回結果集
done!