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


Posted in Javascript onMarch 31, 2015

function addCombinator(matcher, combinator, base)

1、源码

function addCombinator(matcher, combinator, base) {

 var dir = combinator.dir, checkNonElements = base

   && dir === "parentNode", doneName = done++;
 return combinator.first ?

 // Check against closest ancestor/preceding element

 function(elem, context, xml) {

  while ((elem = elem[dir])) {

   if (elem.nodeType === 1 || checkNonElements) {

    return matcher(elem, context, xml);

   }

  }

 } :
 // 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;

      }

     }

    }

   }

  }

 };

}

2、功能

生成关系选择器的执行函数。

3、参数

matcher——位置关系前连续的过滤选择器匹配函数数组,该函数用于匹配通过位置关系获得的节点是否符合选择器要求。在实际执行过程中,该函数可能是关系选择器前已生成的elementMatcher(matchers)。例如:div.map>span,在Sizzle编译遇到>时,会将div.map的编译函数作为第一个参数调用addCombinator函数,用以检查获取的span父节点是否满足div.map这两个条件。

combinator——关系选择器对应Expr.relative中的值,Expr.relative中各种关系选择器的值如下。使用该参数的first属性来确定返回的是仅检查紧邻对象的函数还是遍历所有可能对象的函数。将通过如下代码:elem = elem[dir],获取指定位置关系的节点,其中dir等于combinator.dir。

Expr.relative : {

 ">" : {

  dir : "parentNode",

  first : true

 },

 " " : {

  dir : "parentNode"

 },

 "+" : {

  dir : "previousSibling",

  first : true

 },

 "~" : {

  dir : "previousSibling"

 }

}

base——该参数与combinator.dir一起,确定变量checkNonElement的值,代码如下。该值从字面理解为当前检查的是非DOM元素,就是当elem.nodeType!=1的时候,若该值为true,则会执行匹配函数,否则结束本次循环。

4、返回函数

4.1 若关系选择器是>或+,则返回如下函数:

function(elem, context, xml) {

 while ((elem = elem[dir])) {

  if (elem.nodeType === 1 || checkNonElements) {

   return matcher(elem, context, xml);

  }

 }

}

4.1.1 功能
若检查element类型节点(即checkNonElements==false),迭代获取elem指定位置关系的第一个element类型节点(elem.nodeType == 1),执行匹配函数,检查该节点是否符合要求,若符合返回true,否则返回false;

若检查所有类型节点(即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;

   }

  }

 }

}
Javascript 相关文章推荐
js对数字的格式化使用说明
Jan 12 Javascript
javascript 正则表达式相关应介绍
Nov 27 Javascript
获取内联和链接中的样式(js代码)
Apr 11 Javascript
jquery easyui combox一些实用的小方法
Dec 25 Javascript
Jquery实现自定义tooltip示例代码
Feb 12 Javascript
JQuery为页面Dom元素绑定事件及解除绑定方法
Apr 23 Javascript
jquery实现的鼠标拖动排序Li或Table
May 04 Javascript
js 实现数值的千分位及保存小数方法(推荐)
Aug 01 Javascript
AngularJs Dependency Injection(DI,依赖注入)
Sep 02 Javascript
jQuery模拟完美实现经典FLASH导航动画效果【附demo源码下载】
Nov 09 Javascript
Angular指令封装jQuery日期时间插件datetimepicker实现双向绑定示例
Jan 22 Javascript
2分钟实现一个Vue实时直播系统的示例代码
Jun 05 Javascript
JS显示表格内指定行html代码的方法
Mar 31 #Javascript
jQuery选择器源码解读(七):elementMatcher函数
Mar 31 #Javascript
jQuery选择器源码解读(六):Sizzle选择器匹配逻辑分析
Mar 31 #Javascript
jQuery选择器源码解读(五):tokenize的解析过程
Mar 31 #Javascript
JavaScript制作windows经典扫雷小游戏
Mar 31 #Javascript
jQuery选择器源码解读(四):tokenize方法的Expr.preFilter
Mar 31 #Javascript
JavaScript制作简易的微信打飞机
Mar 31 #Javascript
You might like
用PHP与XML联手进行网站编程代码实例
2008/07/10 PHP
php关于array_multisort多维数组排序的使用说明
2011/01/04 PHP
php生成缩略图示例代码分享(使用gd库实现)
2014/01/20 PHP
PHP实现加强版加密解密类实例
2015/07/29 PHP
PHP环境搭建(php+Apache+mysql)
2016/11/14 PHP
PHP 二维关联数组根据其中一个字段排序(推荐)
2017/04/04 PHP
gearman中任务的优先级和返回状态实例分析
2020/02/27 PHP
javascript import css实例代码
2008/07/18 Javascript
简短几句jquery代码的实现一个图片向上滚动切换
2011/09/02 Javascript
原生Js实现简易烟花爆炸效果的方法
2015/03/20 Javascript
Bootstrap菜单按钮及导航实例解析
2016/09/09 Javascript
jQuery实现导航滚动到指定内容效果完整实例【附demo源码下载】
2016/09/20 Javascript
微信小程序 增、删、改、查操作实例详解
2017/01/13 Javascript
Bootstrap table简单使用总结
2017/02/15 Javascript
jquery加载单文件vue组件的方法
2017/06/20 jQuery
基于jQuery Ajax实现下拉框无刷新联动
2017/12/06 jQuery
jQuery图片查看插件Magnify开发详解
2017/12/25 jQuery
使用 vue 实例更好的监听事件及vue实例的方法
2019/04/22 Javascript
浅谈vue中使用编辑器vue-quill-editor踩过的坑
2020/08/03 Javascript
跟老齐学Python之字典,你还记得吗?
2014/09/20 Python
Python中的包和模块实例
2014/11/22 Python
Python读取和处理文件后缀为.sqlite的数据文件(实例讲解)
2017/06/27 Python
python实现对csv文件的列的内容读取
2018/07/04 Python
用Q-learning算法实现自动走迷宫机器人的方法示例
2019/06/03 Python
Python安装whl文件过程图解
2020/02/18 Python
Python获取对象属性的几种方式小结
2020/03/12 Python
如何在windows下安装Pycham2020软件(方法步骤详解)
2020/05/03 Python
html5-canvas中使用clip抠出一个区域的示例代码
2018/05/25 HTML / CSS
ZINVO手表官网:男士和女士手表
2019/03/10 全球购物
德国净水壶和滤芯品牌:波尔德PearlCo(家用净水器)
2020/04/29 全球购物
芭比波朗加拿大官方网站:Bobbi Brown Cosmetics CA
2020/11/05 全球购物
大客户销售经理职责
2013/12/04 职场文书
电气工程师岗位职责
2014/01/01 职场文书
工程项目建议书范文
2014/03/12 职场文书
幼儿园见习报告范文
2014/10/30 职场文书
中秋节随笔
2015/08/15 职场文书