matcher就是elementMatcher函數的包裝,整個匹配的核心就在這個里面了:
function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; }
我們先來回顧下這個matchers的組合原理,這個地方是最繞的,也是最暈的,所以還是要深入的理解才行哦。
先上個簡單的流程圖:
執(zhí)行分解:
第一步:
div > p + div.aaron input[type="checkbox"]
從右邊剝離出原生API能使用的接口屬性
context.getElementsByTagName( input )
所以找到了input,因為只可以用tag是查詢,但是此時結果是個合集,引入seed的概念,稱之為種子合集。
第二步:
div > p + div.aaron [type="checkbox"]
重組選擇器,踢掉input,得到新的tokens詞法元素哈希表。
第三步:
通過matcherFromTokens函數,然后根據關系選擇器 【">","空","~","+"】拆分分組,因為DOM中的節(jié)點都是存在關系的,所以引入Expr.relative -> first:true 兩個關系的“緊密”程度,用于組合最佳的篩選。
一次按照如下順序解析并且編譯閉包函數,編譯規(guī)則:div > p + div.aaron [type="checkbox"]。
編譯成4組閉包函數,然后在前后在合并組合成一組:
div > p + div.aaron input[type="checkbox"]
先看構造一組編譯函數
A: 抽出div元素,對應的是TAG類型
B: 通過Expr.filter找到對應匹配的處理器,返回一個閉包處理器
如TAG方法:
"TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; },
C:將返回的curry方法放入到matchers匹配器組中,繼續(xù)分解
D:抽出子元素選擇器 '>' ,對應的類型 type: ">"
E:通過Expr.relative找到elementMatcher方法分組合并多個詞素的的編譯函數
function(elem, context, xml) { var i = matchers.length; while (i--) { if (!matchers[i](elem, context, xml)) { return false; } }
所以這里其實就是執(zhí)行了各自Expr.filter匹配中的的判斷方法了,看到這里matcher方法原來運行的結果都是bool值,所以這里只返回了一個組合閉包,通過這個篩選閉包,各自處理自己內部的元素。
F:返回的這個匹配器還是不夠的,因為沒有規(guī)范搜索范圍的優(yōu)先級,所以這時候還要引入addCombinator方法
G:根據Expr.relative -> first:true 兩個關系的“緊密”程度
如果是親密關系addCombinator返回:
function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } }
所以可見如果是緊密關系的位置詞素,找到第一個親密的節(jié)點,立馬就用終極匹配器判斷這個節(jié)點是否符合前面的規(guī)則。
請驗證,完成請求
由于請求次數過多,請先驗證,完成再次請求
打開微信掃碼自動綁定
綁定后可得到
使用 Ctrl+D 可將課程添加到書簽
舉報