下面這個栗子,CSS選擇器它是如何工作的?
.mod-nav h3 span {font-size: 16px;}
如果不知道匹配規則,可能的理解是從左向右匹配:先找到.mod-nav,然后逐級匹配h3、span,在這個過程中如果遍歷到葉子節點都沒有匹配就需要回溯,繼續尋找下一個分支。
但事實上,CSS選擇器的讀取順序是從右向左。
還是上面的選擇器,它的讀取順序變成:先找到所有的span,沿着span的父元素查找h3,中途找到了符合匹配規則的節點就加入結果集;如果直到根元素html都沒有匹配,則不再遍歷這條路徑,從下一個span開始重復這個過程(如果有多個最右節點為span的話)。
在某條CSS規則下(比如.mod-nav h3 span),會形成一條符合規則的索引樹,樹由上至下的節點是規則中從右向左的一個個選擇符匹配的節點。索引樹遍歷的具體過程可以看寒冬大大的一段視頻。
為什么從右向左的規則要比從左向右的高效?

假如DOM的結構如上圖,匹配規則是.mod-nav h3 span。
若從左向右的匹配,過程是:從.mod-nav開始,遍歷子節點header和子節點div,然后各自向子節點遍歷。在右側div的分支中,最后遍歷到葉子節點a,發現不符合規則,需要回溯到ul節點,再遍歷下一個li-a,假如有1000個li,則這1000次的遍歷與回溯會損失很多性能。
再看看從右至左的匹配:先找到所有的最右節點span,對於每一個span,向上尋找節點h3,由h3再向上尋找class=mod-nav的節點,最后找到根元素html則結束這個分支的遍歷。
很明顯,兩種匹配規則的性能差別很大。之所以會差別很大,是因為從右向左的匹配在第一步就篩選掉了大量的不符合條件的最右節點(葉子節點);而從左向右的匹配規則的性能都浪費在了失敗的查找上面。
當然這是比較明顯情況,如果在葉子上存在多個不符合條件的span,從右向左的規則也會走一些彎路(這時就需要優化CSS選擇器了)。但平均來說它還是更高效,因為大多時候,一個DOM樹中,符合匹配條件的節點(如.mod-nav h3 span)遠遠遠遠少於不符合條件的節點。
jQuery從1.3版本開始使用的Sizzle引擎,它按照了CSS選擇器的匹配規則(從右至左)進行DOM元素的查找與匹配(當然其中做了很多優化),性能得到了很大的提升。
歡迎批評指正。