matcher就是elementMatcher函数的包装,整个匹配的核心就在这个里面了:
function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; }
我们先来回顾下这个matchers的组合原理,这个地方是最绕的,也是最晕的,所以还是要深入的理解才行哦。
先上个简单的流程图:
执行分解:
第一步:
div > p + div.aaron input[type="checkbox"]
从右边剥离出原生API能使用的接口属性
context.getElementsByTagName( input )
所以找到了input,因为只可以用tag是查询,但是此时结果是个合集,引入seed的概念,称之为种子合集。
第二步:
div > p + div.aaron [type="checkbox"]
重组选择器,踢掉input,得到新的tokens词法元素哈希表。
第三步:
通过matcherFromTokens函数,然后根据关系选择器 【">","空","~","+"】拆分分组,因为DOM中的节点都是存在关系的,所以引入Expr.relative -> first:true 两个关系的“紧密”程度,用于组合最佳的筛选。
一次按照如下顺序解析并且编译闭包函数,编译规则: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匹配器组中,继续分解
D:抽出子元素选择器 '>' ,对应的类型 type: ">"
E:通过Expr.relative找到elementMatcher方法分组合并多个词素的的编译函数
function(elem, context, xml) { var i = matchers.length; while (i--) { if (!matchers[i](elem, context, xml)) { return false; } }
所以这里其实就是执行了各自Expr.filter匹配中的的判断方法了,看到这里matcher方法原来运行的结果都是bool值,所以这里只返回了一个组合闭包,通过这个筛选闭包,各自处理自己内部的元素。
F:返回的这个匹配器还是不够的,因为没有规范搜索范围的优先级,所以这时候还要引入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 ); } } }
所以可见如果是紧密关系的位置词素,找到第一个亲密的节点,立马就用终极匹配器判断这个节点是否符合前面的规则。
请验证,完成请求
由于请求次数过多,请先验证,完成再次请求
打开微信扫码自动绑定
绑定后可得到
使用 Ctrl+D 可将课程添加到书签
举报