7-13 Sizzle超级匹配器(中)
本节编程练习不计算学习进度,请电脑登录imooc.com操作

Sizzle超级匹配器(中)

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 );
        }
    }
}

所以可见如果是紧密关系的位置词素,找到第一个亲密的节点,立马就用终极匹配器判断这个节点是否符合前面的规则。

任务

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <script src="http://code.jquery.com/jquery-latest.js"></script>
  6. <title></title>
  7. </head>
  8. <body>
  9.  
  10.  
  11. <button id="test1">超级匹配器,点击选中checked</button>
  12.  
  13. <div id="text">
  14. <div class="Aaron">
  15. <input type="checkbox" name="readme" />
  16. <input type="checkbox" name="ttt" />
  17. <input type="checkbox" name="aaa" />
  18. <p>Sizzle</p>
  19. </div>
  20. </div>
  21.  
  22.  
  23. <script type="text/javascript">
  24.  
  25. var filter = {
  26. ATTR: function(name, operator,check) {
  27. return function(elem) {
  28. var attr = elem.getAttribute(name)
  29. if (operator === "=") {
  30. if(attr === check){
  31. return true
  32. }
  33. }
  34. return false;
  35. }
  36. },
  37. TAG: function(nodeNameSelector) {
  38. return function(elem) {
  39. return elem.nodeName && elem.nodeName.toLowerCase() === nodeNameSelector;
  40. };
  41. }
  42. }
  43.  
  44.  
  45. function addCombinator(matcher) {
  46. return function(elem, context, xml) {
  47. while ((elem = elem['parentNode'])) {
  48. if (elem.nodeType === 1) {
  49. //找到第一个亲密的节点,立马就用终极匹配器判断这个节点是否符合前面的规则
  50. return matcher(elem);
  51. }
  52. }
  53. }
  54. }
  55.  
  56.  
  57. function elementMatcher(matchers) {
  58. return matchers.length > 1 ?
  59. function(elem, context, xml) {
  60. var i = matchers.length;
  61. while (i--) {
  62. if (!matchers[i](elem, context, xml)) {
  63. return false;
  64. }
  65. }
  66. return true;
  67. } :
  68. //单个匹配器的话就返回自己即可
  69. matchers[0];
  70. }
  71.  
  72.  
  73.  
  74. function matcherFromTokens(tokens){
  75. var len = tokens.length;
  76. var matcher, matchers = [];
  77. for (i = 0; i < len; i++) {
  78. if (tokens[i].type === " ") {
  79. matchers = [addCombinator(elementMatcher(matchers), matcher)];
  80. } else {
  81. matcher = filter[tokens[i].type].apply(null, tokens[i].matches);
  82. matchers.push(matcher);
  83. }
  84. }
  85. return elementMatcher(matchers);
  86. }
  87.  
  88.  
  89. /**
  90. * 超级匹配器
  91. * @return {[type]} [description]
  92. */
  93. function matcherFromGroupMatchers(elementMatchers) {
  94. var superMatcher = function(seed) {
  95. var results = [];
  96. var matcher, elem;
  97. for (var i = 0; i < seed.length; i++) {
  98. matcher = elementMatchers[0];
  99. var elem = seed[i];
  100. if (matcher(elem)) {
  101. results.push(elem);
  102. break;
  103. }
  104. }
  105. return results;
  106. };
  107. return superMatcher;
  108. }
  109.  
  110.  
  111.  
  112.  
  113. function compile() {
  114. //种子合集
  115. var seed = document.querySelectorAll('input')
  116. //选择器
  117. var selector = "Aaron [name=ttt]";
  118. var elementMatchers = [];
  119. var match = [{
  120. matches: ["div"],
  121. type: "TAG",
  122. value: "Aaron"
  123. }, {
  124. type: " ",
  125. value: " "
  126. }, {
  127. matches: ["name", "=", "ttt"],
  128. type: "ATTR",
  129. value: "[name=ttt]"
  130. }]
  131.  
  132. elementMatchers.push(matcherFromTokens(match));
  133.  
  134.  
  135. //超级匹配器
  136. var cached = matcherFromGroupMatchers(elementMatchers)
  137. results = cached(seed)
  138.  
  139. results[0].checked = 'checked'
  140. }
  141.  
  142.  
  143. $('#test1').click(function() {
  144. compile();
  145. })
  146.  
  147.  
  148.  
  149.  
  150.  
  151. </script>
  152.  
  153. </body>
  154. </html>
下一节