主要流程与正则

表达式分块

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g;

这个正则比较长,主要是用来分块和一步预处理。

1、

2、

3、

4、

'div#test + p > a.tab'    --> ['div#test','+','p','>','a.tab']

从表达式提取出相应的类型:

这个需要对应jQuery的选择器来看,共7种
ID选择器,CLASS选择器,TAG选择器,ATTR属性选择器,CHILD子元素选择器,PSEUDO伪类选择器,POS位置选择器

判断的方法还是正则,具体正则如下:

ID    : /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS : /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME  : /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR  : /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG   : /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD : /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS   : /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/

ID:

CLASS:

NAME:

TAG:

ATTR:

POS:

PSEUDO:

正则小提示:

?非贪婪量词
\3匹配分
?=正向预查

这些正则可能一开始不好看,但是对应到具体的jQuery选择器就比较好理解了:

POS ——  :first :nth() :last :gt :lt :even :odd  这些是Sizzle新加的,跟CSS无关

其他的倒是跟CSS基本无异,需要注意的是,由于PSEUDO的存在,同一个表达式可能同时匹配多个类型,这个后面的filter部分会提到。

上面的正则字符串保存在Expr的match属性中,

Expr = {    match:{//ID:....    }}

这部分正则并没有直接使用,进行了进一步的处理
第一、每个字符串后面都增加了一个判断,用来确保匹配结果,末尾不包含)或者}

/#((?:[\w\u00c0-\uFFFF\-]|\\.)+))/变成/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?![^\[]*\])(?![^\(]*\)/

第二、同时Sizzle会检测转义字符,因此各部分头部都增加了一个捕获组用来保存目标字符串前面的部分,
在这一步的时候,由于在头部增加了一分组,因此原正则字符串中的\3等符号必须顺次后移。

/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?![^\[]*\])(?![^\(]*\)/变成/(^(?:.|\r|\n)*?)#((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?![^\[]*\])(?![^\(]*\)/

/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/变成/(^(?:.|\r|\n)*?):((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\3\))?(?![^\[]*\])(?![^\(]*\)/

对应到源码中就是:

var    fescape = function(all, num){return "\\" + (num - 0 + 1);    };

for ( var type in Expr.match ) {    Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );    Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );}

Expr.leftMatch 中保存的是处理过后的正则部分,这么做的另一个好处就是避免每次匹配都去创建一个新的RegExp对象

回到主流程

函数介绍:

var Sizzle = function( selector, context, results, seed ){}
Sizzle有四个参数:
  selector :选择表达式
  context :上下文
  results  :结果集
  seed     :候选集

实例说明:

Sizzle('div',#test,[#a,#b],[#c,#d,#e])就是在集合[#c,#d,#e]中查找满足条件(在#test范围中并标签名为div)的元素,然后将满足条件的结果存入[#a,#b]中,假设满足条件的有#d,#e,最后获得就是[#a,#b,#d,#e]。

代码示例:

var Sizzle = function( selector, context, results, seed ){var soFar = selector,        extra    ,//extra用来保存并联选择的其他部分,一次只处理一个表达式        parts = [],        m;do {        chunker.exec( "" );     //这一步主要是将chunker的lastIndex重置,当然直接设置chunker.lastIndex效果也一样        m = chunker.exec( soFar );if ( m ) {            soFar = m[3];            parts.push( m[1] );if ( m[2] ) {       //如果存在并联选择器,就中断,保存其他的选择器部分。                extra = m[3];break;            }        }    } while ( m );}

对于'div#test + p > a.tab'parts结果就是['div#test','+','p','>','a.tab']

分块之后,下一步就是决定的选择器的顺序,可以对照(一)的说明,构建两个分支:

if ( parts.length > 1 && origPOS.exec( selector ) ) {//自左向右,判断标准就是存在关系选择符同时有位置选择符,因为如果只是类似div#test的选择表达式,就不存在顺序的问题。

}else{//其他,自右向左}

【说明:origPOS保存的是Expr.match.POS,源码901行】

先看普通的(自右向左)情况
然后就是ID的问题,第一个选择表达式含有id就重设context,
当存在context的时候【没有的话就不用找了,因为肯定没结果】,

在重设了contexr之后,既然是自右向左,第一步就是获取等待过滤的集合,

ret = seed ?{ expr: parts.pop(), set: makeArray(seed) } :Sizzle.find( parts.pop(), context);//Sizzle.find负责查找set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;//Sizzle.filter负责过滤

有候选集seed的时候直接获得候选集,没有的时候获取最右边一个选择符的结果集。

后面的过程就是依次取出parts中的选择符,在set中查找,过滤,直到全部查完

        while ( parts.length ) {                Expr.relative[ cur ]( checkSet, context, contextXML );//context代表上下文,并非源码中的参数            }

实例说明:

['div#test','+','p','>','a.tab']处理流程
第一步、没有候选集seed,第一项'div#test'中含有id信息,最后一项'a.tab'中不含有id信息,因此重设content=Sizzle.find('div#test',document)
第二步、剩余部分为['+','p','>','a.tab'],没有候选集seed,先获取等待过滤[标签名为a]的集合A,在集合A中过滤类名为tab的集合B
第三步、剩余部分为['+','p','>'],进行基于关系的过滤,这是一个逆向过程,假设第二步中的B=[#a,#b,#c,#d],先在查找直接父节点是p的元素,获得集合
    C = [#a,#b,false,false],然后获取紧挨第一步中content的元素,获得集合D = [#a,false,false,false]
第四步、取得D中不为false的部分,获得此次选择的集合E=[#a];并入结果集result中。
第五步、按照上面的规则,处理并联选择表达式的第二部分。

讨论

关于context的选择

在没有候选集,需要重设ID情况有那些呢?
  1、div#id_1 a#id_2
  2、div#id_1 a
  3、div a#id_2
Sizzle中,只有情况(2)下才去设置context

第二步中、关系选择符“+”和“~”表示的是同层级的关系,因此,context【查找范围】会被设置成context.parentNode

实例说明:

<body>    <div id="test_a">        <p class="tab" id="a1">a1</p>        <p class="tab" id="a1">a2</p>        <p class="tab" id="a1">a3</p>    </div>    <div id="test_b">        <p class="tab" id="b1">b1</p>        <p class="tab" id="b1">b2</p>        <p class="tab" id="b1">b3</p>    </div></body>

选择表达式'div#test_a ~ div'
第一步重设context为div#test_a
第二步中如果直接执行(div#test_a).getElemnetsByTagName('div')显然是没有结果的,此时操作根本就是错误的。
因此,应该执行的是(div#test_a).parentNode.getElemnetsByTagName('div').然后再进行第三步。

接下来是Sizzle.find流程分析:《Sizzle引擎--原理与实践(三)》

转载请注明来自小西山子【http://www.cnblogs.com/xesam/】
本文地址:http://www.cnblogs.com/xesam/archive/2012/02/15/2352471.html

转载于:https://www.cnblogs.com/xesam/archive/2012/02/15/2352471.html

Sizzle引擎--原理与实践(二)相关推荐

  1. Linux 快照 (snapshot) 原理与实践(二) 快照功能实践

    文章目录 0. 概要 1. 准备演示数据 2. 创建 snapshot-origin 目标 3. 创建 snapshot 目标 4. 验证 COW 操作 4.1 第一次写数据 4.2 第二次写数据 5 ...

  2. osgEarth的Rex引擎原理分析(二十五)地形瓦片大小尺寸和LOD的关系

    目标:(十八)中的问题55 osgEarth::TerrainOption中_tileSize默认大小为17,LOD的默认范围为0-23,这两个值的关系是什么? 还有瓦片的像素尺寸_tilePixel ...

  3. osgEarth的Rex引擎原理分析(一二六)rex瓦片组织方式

    目标:(一二五)中问题212 通过如下确定瓦片的组织方式 ,核心是profile osgEarth/Map.cpp void Map::calculateProfile() {// collect t ...

  4. JQuery - Sizzle选择器引擎原理分析

    说明:14年学习的jquery源码,搬到这里供大家交流.原文地址:https://segmentfault.com/a/1190000003933990 一.前言 Sizzle原来是jQuery里面的 ...

  5. #20155235 《网络攻防》 实验二 后门原理与实践

    20155235 <网络攻防> 实验二 后门原理与实践 实验目的 建立一个后门连接是如此的简单,功能又如此强大.通过亲手实践并了解这一事实,从而提高自己的安全意识 . 实验内容 (1)使用 ...

  6. 第十二课:Sizzle引擎详解

    这篇博客难度太大,跟前端开发其实没什么关系,如果你想成为大牛,那就去了解下吧.如果你还不想,那可以忽略,毕竟面试官也不会问到这里来,因为他也不太懂.呵呵. Sizzle引擎是jQuery的选择器,它大 ...

  7. PHP模板引擎的原理与实践

    0x00 模板引擎的原理 模板引擎就是在模板文件中使用一系列提前约定好的标签代替原生PHP代码,通过访问一个PHP的入口文件,会有一个PHP编译文件根据约定替换模板内标签以及标签内变量,最终将模板文件 ...

  8. 2017-2018-2 20155228 《网络对抗技术》 实验二:后门原理与实践

    2017-2018-2 20155228 <网络对抗技术> 实验二:后门原理与实践 实验内容 使用netcat获取主机操作Shell,cron启动 (0.5分) 使用socat获取主机操作 ...

  9. 西瓜视频稳定性治理体系建设二:Raphael 原理及实践

    摘要 Raphael [1]是西瓜视频基础技术团队开发的一款 native 内存泄漏检测工具,广泛用于字节跳动旗下各大 App 的 native 内存泄漏治理,收益显著.工具现已开源,本文将通过原理. ...

最新文章

  1. mysql 设置 row格式binlog_MySql:Row 格式的 Binlog 解析
  2. MySQL修改数据库:ALTER DATABASE用法简介
  3. 手把手教你搭建Hadoop生态系统伪分布式集群
  4. 使用数据库做“非授权”的事情
  5. 简单使用ansible-playbook
  6. 从PHPer到Gopher要经历的一些转变
  7. 常用加密算法的Java实现(一)
  8. mvc% html.%,MVC的html.doc
  9. 17. 促销,市场营销和内容页
  10. QOS 原理阶段学习总结
  11. python计算正方形、立方体、圆、球的面积和体积
  12. bing翻译接口appID申请教程【TranslateApiException: AppId is over the quota 】
  13. 分析公司盈利能力的方法
  14. 计算机研究生个人简历,美国计算机研究生申请个人简历这样填比模板更出众!...
  15. 互联网创业赚钱规则,彻底释放自己的价值吧!丨国仁网络
  16. 谨慎处理单片机中断,中断等价于比主程序优先级更高的线程
  17. Asp.Net Web Api 部署------在云服务器IIS上部署Web Api程序
  18. pc2环境配置指南(windows)
  19. 计算机表格大小怎么调整,excel把表格拉大_excel调整单元格大小的方法步骤详解...
  20. HADOOP数据仓库

热门文章

  1. Cassandra使用的各种策略
  2. 【poj3208-Apocalypse Someday】数位DP
  3. 《JavaScript高级程序设计》心得笔记-----第四篇章
  4. #研发解决方案#基于Apriori算法的Nginx+Lua+ELK异常流量拦截方案
  5. 编译linux内核步骤
  6. 根据客户中英文系统进入中英文页面
  7. C语言基本数据结构之四(线性,对分,二叉树查找及二叉树删除)
  8. ANDROID 中UID与PID的作用与区别
  9. 【大厂攻略】Android开发3年当了2年咸鱼每天CRUD,复习2个月幸运拿下美团offer!
  10. 深入理解WMS(三):剖析Activity,View,Window之间的关系