jQuery选择器源码解读(八):addCombinator函数(2)

若检查所有类型节点(即checkNonElements==true),获取elem指定位置关系的紧邻节点,执行匹配函数,检查该节点是否符合要求,若符合返回true,否则返回false;
有些人或许会问,不是说是紧邻关系吗?那代码中为何要出现迭代获取这一过程呢?这是因为,个别浏览器会把节点文本之间的换行符看成是TextNode,故在处理过程中,需要跳过这些节点,直到下一个element节点。
4.1.2 参数
elem——待检查的单个节点元素。

context——执行整个选择器字符串匹配的上下文节点,大部分时候是没有用途。

xml——当前搜索对象是HTML还是XML文档,若是HTML,则xml参数为false。

4.2  若关系选择器是~或空格,则返回如下函数:

复制代码 代码如下:


//Check against all ancestor/preceding elements
function(elem, context, xml) {
 var data, cache, outerCache, dirkey = dirruns + " " + doneName;

// We can't set arbitrary data on XML nodes, so they don't
 // benefit from dir caching
 if (xml) {
  while ((elem = elem[dir])) {
   if (elem.nodeType === 1 || checkNonElements) {
    if (matcher(elem, context, xml)) {
     return true;
    }
   }
  }
 } else {
  while ((elem = elem[dir])) {
   if (elem.nodeType === 1 || checkNonElements) {
    outerCache = elem[expando] || (elem[expando] = {});
    if ((cache = outerCache[dir])
      && cache[0] === dirkey) {
     if ((data = cache[1]) === true
       || data === cachedruns) {
      return data === true;
     }
    } else {
     cache = outerCache[dir] = [ dirkey ];
     cache[1] = matcher(elem, context, xml)
       || cachedruns;
     if (cache[1] === true) {
      return true;
     }
    }
   }
  }
 }
};

4.2.1 功能

若检查的是XML文档,则其过程与4.1返回函数一致,见上述代码中if ( XML ) { ... }中大括号内的代码。

若是HTML文档,则根据matcher匹配当前元素,若匹配成功,返回true;否则返回false。

4.2.2 参数
elem——待检查的单个节点元素。

context——执行整个选择器字符串匹配的上下文节点,大部分时候是没有用途。

xml——当前搜索对象是HTML还是XML文档,若是HTML,则xml参数为false。

4.2.3 代码说明

内部变量

dirkey——缓存节点检测结果用的键。在一次执行过程中,若一个节点被检查过,则会在这个节点的dirkey属性(属性名称为dirkey的值)中记录下检测结果(true或false),那么在本次执行过程中,再次遇到该节点时,不需要再次检测了。之所以需要缓存,因为多个节点会存在同一个父节点或兄弟节点,利用缓存可以减少检测的次数,提高性能。

dirruns——每次执行通过matcherFromGroupMatchers组织的预编译代码时都会产生一个伪随机数,用以区别不同的执行过程。
doneName——每次执行addCombinator函数时,done变量都会加1,用以区别生成的不同的位置关系匹配函数。

cachedruns——用来记录本次匹配是第几个DOM元素。例如:div.map>span,有3个元素符合span选择器,则针对每个元素执行>匹配函数时,cachedruns依次为0、1、2。cachedruns的作用按照代码可以直接理解为在一个执行过程中,针对同一个元素使用elementMatchers进行匹配过程中,再次遇到同一个元素时,可以直接从获取不匹配的结果,但是,我想不出哪个情况下会发生这种事情。若有人遇到,请告知,多谢!

代码解释

复制代码 代码如下:


while ((elem = elem[dir])) {
 if (elem.nodeType === 1 || checkNonElements) {
  // 若elem节点的expando属性不存在,则赋予空对象,并同时赋予outerCache
  // 若elem节点的expando属性存在,则将其值赋予outerCache
  outerCache = elem[expando] || (elem[expando] = {});
  /*
   * 若outCache[dir]有值,且其第一个元素等于当前的dirkey,
   *     则说明当前位置选择器在本次执行过程中已检测过该节点,执行if内的语句,从缓存中直接获取结果
   * 若outCache[dir]不存在,或第一个元素不等于当前的dirkey,
   *     则说明当前位置选择器在本次执行过程中还未检测过该节点,执行else内的语句,匹配节点并将结果放入缓存
   */
  if ((cache = outerCache[dir])
    && cache[0] === dirkey) {
   // 若缓存中检测结果等于true或cachedruns的值,则返回检测结果(非true皆为false),
   // 否则继续循环获取上一个符合位置关系的节点进行匹配
   if ((data = cache[1]) === true
     || data === cachedruns) {
    return data === true;
   }
  } else {
   // 将数组[ dirkey ]赋予outerCache[dir]及cache
   cache = outerCache[dir] = [ dirkey ];
   // 将匹配成功,将true赋予cache[1],否则将cachedruns的值赋予cache[1]
   cache[1] = matcher(elem, context, xml)
     || cachedruns;
   // 若匹配结果为true,则返回true,否则继续循环获取上一个符合位置关系的节点进行匹配
   if (cache[1] === true) {
    return true;
   }
  }
 }
}

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgjpdg.html