這篇博客難度太大,跟前端開發其實沒什麼關係,若是你想成爲大牛,那就去了解下吧。若是你還不想,那能夠忽略,畢竟面試官也不會問到這裏來,由於他也不太懂。呵呵。前端
Sizzle引擎是jQuery的選擇器,它大部分操做都是從右到左進行選擇,特殊選擇符會從左到右。用戶輸入$("div"),$("div p.class"),$("div [attr=val] :checked")等各類複雜的選擇符,它都能選擇到用戶想要取到的元素節點。面試
Sizzle的總體結構以下:數組
(1)Sizzle主函數,裏面包含選擇符的切割,內部循環調用主查找函數,主過濾函數,最後是去重過濾。瀏覽器
(2)其餘輔助函數,如 uniqueSort, matches ,matchesSelector。緩存
(3)Sizzle.find主查找函數函數
(4)Sizzle.filter主過濾函數優化
(5)Sizzle.selectors 包含各類匹配用的正則,過濾用的正則,分解用的正則,預處理函數,過濾函數。spa
(6)根據瀏覽器的特徵設計makeArray,sortOrder,contains等方法。prototype
(7)根據瀏覽器的特徵重寫Sizzle.selectors中的部分查找函數,過濾函數,查找次序。設計
(8)若瀏覽器支持querySelectorAll,那麼用它重寫Sizzle,將原來的Sizzle做爲後備方案包裹在新的Sizzle裏面。
(9)其餘輔助函數,如:isXML,posProcess。
Sizzle.find主查找函數和Sizzle.filter過濾函數實現原理:
對js原生的4大查找函數,getElementById(針對id),getElementsByName(針對name),getElementsByTagName(針對標籤名tagName,好比div,p),getElementsByClassName(針對class),進行一層封裝,瀏覽器支持的話,就返回數組或者NodeList,不支持的,就返回undefined。
這裏須要講一下種子集,
種子集就是經過最右邊的選擇器組獲得的元素集合。好比:"div.aaa span.bbb",最右邊的選擇器組就是"span.bbb",這時引擎會根據瀏覽器的支持狀況選擇getElementsByTagName(span)或getElementsByClassName(bbb)獲得一組元素,而後再經過class(bbb)或tagName(span)進行過濾,這時獲得的集合就是種子集。種子集是分兩步篩選出來的,首先,經過Sizzle.find獲得一個大致的結果,而後經過Sizzle.filter過濾。那咱們是先取span,仍是.bbb呢?這裏有一個準則,要確保咱們後面的映射集(當咱們取得種子集後,會將種子集複製一份,這就是映射集)最小。爲了達到此目的,這裏有一個優化,原生選擇器的調用順序被放在一個Sizzle.selectors.order的數組中,對於低版本瀏覽器,其順序爲id,name,tagName,對於支持getElementsByClassName的瀏覽器,順序爲id,class,name,tagName。由於id只返回一個元素,class與樣式相關,不是每一個元素都有這個類名的,name屬性使用到的概率比較少,而tagName排除的元素比較少。因此Sizzle.find就會根據Sizzle.selectors.order數組,依次調用正則,從最右的選擇器中切下須要的部分,找到粗糙的節點集合。(針對"span.bbb",id調用正則時,找不到,而後class,調用正則,找到.bbb,所以就調用getElementsByClassName(bbb)獲得一組數據,最後經過Sizzle.filter過濾取到的數據,過濾條件是tagName(span))
映射集,
當咱們取得種子集後,會將種子集複製一份,這就是映射集。種子集是由一個選擇器組選出來的,這時若是選擇符不爲空(前面是"div.aaa"),必然往左就是關係選擇器(父親,兄弟,後代),關係選擇器會讓引擎去選取其兄長或父親,把這些元素置換到映射集對等的位置上(個數不變,所以映射集和種子集的數量老是至關)。而後到下一個選擇器組時("div.aaa"),就是過濾操做了。主過濾函數Sizzle.filter會調用Sizzle.selectors下的N個過濾函數對這些元素進行檢測,將不符合的元素替換爲false。所以到最後要去重排時,映射集是一個包含布爾值與元素節點的數組。
下面就是根據瀏覽器的特徵進行優化:
IE6,7下getElementById有bug。須要重寫。
IE6-IE8下,Array.prototype.slice.call沒法切割NodeList。須要重寫makeArray。jQuery中直接用循環,把類數組轉化成數組。
IE6-IE8下,getElementsByTagName("*"),會混雜註釋節點。
這裏你們可能會提出如今有些瀏覽器支持querySelectorAll方法,這是原生的,能夠用來選擇元素。
在Sizzle中,當瀏覽器支持querySelectorAll方法時,會重寫Sizzle。可是在重寫時,會根據不一樣狀況提出各類提速方案:
(1)getElementById仍是比querySelectorAll速度快,由於getElementById只返回一個元素,並且內部作了緩存,可是querySelectorAll會返回擁有這個id值的多個元素,儘管頁面id通常是惟一的,但若是出現了多個一樣id的狀況下,getElementById仍是隻返回一個元素,而querySelectorAll會返回多個。
(2)getElementsByTagName內部也使用了緩存,並且返回的是NodeList對象,querySelectorAll返回的是一個StaticNodeList對象,前面是動態的,後面是靜態的。區別在於:document.getElementsByTagName("div") == document.getElementsByTagName("div"),返回真,document.querySelectorAll("div") == document.querySelectorAll("div"),返回false.返回true的,意味着它們拿到的同是cache引用。返回false意味着每次返回都是不同的object。數據代表:建立一個動態的NodeList對象比建立一個靜態的StaticNodeList對象快90%.
加油!