`
zha_zi
  • 浏览: 593165 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

Sizzle 引擎--原理与实践2

 
阅读更多

主要流程与正则

表达式分块

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').然后再进行第三步。

 

分享到:
评论

相关推荐

    jQuery中的Sizzle引擎分析demo

    在这个"jQuery中的Sizzle引擎分析demo"中,我们将深入探讨Sizzle的工作原理以及如何通过实例来理解它。 Sizzle引擎的设计目标是提供一个高性能的选择器解析和执行环境,它独立于jQuery之外,可以单独使用。Sizzle...

    前端项目-epitome.zip

    - 该框架的核心特性包括:选择器引擎(Sizzle)、类系统、对象继承、事件处理、动画效果、Ajax通信、JSON解析等。 - MVP框架选择MooTools作为基础,是因为它提供了构建复杂前端应用所需的基础设施。 2. **MVP模式...

    jQuery内核详解与实践_1.pdf

    ### jQuery内核详解与实践 #### 一、引言 jQuery作为一款流行的JavaScript库,在前端开发领域具有举足轻重的地位。它简化了许多常见的JavaScript任务,如选择元素、动画、事件处理等,使得开发者能够更加高效地...

    jQuery-, jQuery源码解读 -- jQuery v1.10.2.zip

    2. **选择器引擎Sizzle** jQuery的选择器引擎Sizzle是其强大功能的关键之一。Sizzle支持CSS1-CSS3的选择器,并能有效地匹配和选取DOM元素。通过阅读源码,我们可以学习到如何编写高效的选择器,以及如何利用Sizzle...

    jQuery表单选择器源码

    学习资源与实践 要深入理解jQuery表单选择器的源码,可以阅读`jQuery源码`和`Sizzle源码`,同时结合实际项目进行实践。`第7章 表单选择器.pdf`文档可能是对这部分内容的一个详细讲解,建议配合源码阅读,加深理解...

    jQuery1.9中文和英文CHM手册

    3. **选择器引擎Sizzle** - jQuery 1.9继续使用Sizzle作为其选择器引擎,能高效地选取DOM元素,支持CSS1至CSS3的选择器语法。 4. **DOM操作** - 包括`.append()`, `.prepend()`, `.before()`, `.after()`, `.remove...

    妙味课堂--jQuery源码

    2. **选择器引擎Sizzle** Sizzle是一个独立于jQuery的选择器库,支持CSS1至CSS3的选择器。它采用了正则表达式和分治策略来解析和执行选择器,大大提升了查询性能。Sizzle的解析过程包括预处理、解析、测试和匹配四...

    novice to ninja jquery source code

    1. **选择器引擎Sizzle**:jQuery选择器是其强大功能的关键,它基于Sizzle引擎,能处理CSS1至CSS3的选择器。Sizzle引擎的高效解析和匹配算法是理解和优化jQuery性能的重要一环。 2. **DOM操作**:jQuery提供了丰富...

    jqery源码逐步解析视频(高清)

    - **Sizzle选择器引擎**:jQuery内部使用的选择器引擎,它独立于jQuery核心之外,可以单独使用。 - **选择器解析过程**:从左到右逐个解析选择器中的每个元素,通过递归算法实现高效查找。 - **优化技巧**:利用缓存...

    jquery权威指南

    - **核心函数实现**:深入理解如选择器引擎Sizzle的工作原理,以及动画实现的细节。 通过阅读《jQuery权威指南》,读者将能够熟练运用jQuery进行高效的前端开发,并对jQuery的内部机制有深入的理解,提升Web开发...

    jquery基础入门

    随着对jQuery的深入,可以学习更高级的主题,如性能优化、插件开发、使用Sizzle选择器引擎等,进一步提升技能水平。 总结,jQuery是前端开发中强大的工具,通过学习其API和基本概念,可以大大提高开发效率,实现...

    Packt - jQuery 1.4 Reference Guide (January 2010) (ATTiCA)

    1. **CSS选择器引擎的改进**:jQuery 1.4中的Sizzle CSS选择器引擎得到了进一步的优化,提高了DOM查询的速度和效率。 2. **新的动画效果**:新增了更多的动画选项,如`slideToggle()`、`animate()`等,使得页面元素...

    JQuery1.4 文档及源码

    - **源码结构**: `jquery-1.4.4.js` 是 jQuery 1.4.4 版本的源码文件,它由多个核心模块组成,如选择器引擎(Sizzle)、事件处理、动画效果、Ajax 请求等。 - **选择器实现**: jQuery 1.4 使用 Sizzle 作为其选择...

    JQuery王兴魁源码

    5. **选择器引擎Sizzle**:jQuery选择器的强大功能源于其内置的选择器引擎Sizzle,学习Sizzle的工作原理能帮助我们更好地利用jQuery选择器。 6. **API设计**:研究源码可以让我们理解jQuery是如何设计其API的,这对...

    jquery-source-code-analysis:这是学习jquery源代码的记录-jquery source code

    2. **选择器引擎(Sizzle)**: - jQuery依赖于Sizzle选择器引擎来高效地选取DOM元素。Sizzle支持CSS1到CSS3的选择器,并提供了一套快速的匹配算法。 3. **DOM操作**: - jQuery提供了丰富的DOM操作方法,如`...

    JQUERY权威指南及源代码

    - **选择器引擎Sizzle**:jQuery选择器引擎Sizzle能解析CSS选择器并高效地找到匹配的DOM元素,它的实现涉及到正则表达式、缓存机制等。 - **链式调用**:jQuery对象是链式调用的基础,每个方法返回的都是jQuery...

    锋利的jQuery源码

    1. **选择器引擎(Sizzle)**:jQuery选择器是其强大功能的基础,它基于Sizzle引擎实现。Sizzle支持CSS1到CSS3的选择器,并且在没有浏览器内置querySelectorAll方法的情况下提供高性能的选择功能。 2. **链式调用**...

    prototype源码

    Sizzle 引擎在源码中的实现涉及到字符串解析、DOM 遍历等复杂逻辑,是理解浏览器与 JavaScript 交互的关键。 7. **模块化设计** Prototype 使用模块化设计,将不同功能划分为独立的模块,如 `Form`、`Ajax`、`...

Global site tag (gtag.js) - Google Analytics