1、浏览器对css选择器采取逆向(从右向左)解析的原因:

  如果正向解析,例如「div div p em」,我们首先就要检查当前元素到 html 的整条路径,找到最上层的div,再往下找,如果遇到不匹配就必须回到最上层那个 div,往下再去匹配选择器中的第一个 div,回溯若干次才能确定匹配与否,效率很低。

  逆向匹配则不同,如果当前的 DOM 元素是 div,而不是 selector 最后的 em,那只要一步就能排除。只有在匹配时,才会不断向上找父节点进行验证。找到所有的em之后,再通过查找他的父元素是不是p来进行过滤。

2、Sizzle如果分解用户传入的css选择器字符串

以”div > div.cl p span.red“为例

  在Sizzle内部封装了一个方法,该方法负责将css选择器分解为一个数组。数组中的每一项是一个对象,格式如下:

    {

      "type" : "CLASS",

      "value" : ".red",
      "matchs" : " "

    }

  看一下tokenize的源码:

  // 假设传入进来的选择器是:div > p + .cl[type="checkbox"], #id:first-child
  // 这里可以分为两个规则:div > p + .aaron[type="checkbox"] 以及 #id:first-child
  // 返回的需要是一个Token序列
  // Sizzle的Token格式如下 :{value:'匹配到的字符串', type:'对应的Token类型', matches:'正则匹配到的一个结构'}
  function tokenize( selector, parseOnly ) {
    var matched, match, tokens, type,
    soFar, groups, preFilters,
    cached = tokenCache[ selector + " " ];
    // 这里的soFar是表示目前还未分析的字符串剩余部分
    // groups表示目前已经匹配到的规则组,在这个例子里边,groups的长度最后是2,存放的是每个规则对应的Token序列

    // 如果cache里边有,直接拿出来即可
    if ( cached ) {
      return parseOnly ? 0 : cached.slice( 0 );
    }

    // 初始化
    soFar = selector;

    // 这是最后要返回的结果,一个二维数组

    // 有多少个并联选择器,里面就有多少个数组,数组里面是拥有value与type的对象
    groups = [];

    // 这里的预处理器为了对匹配到的Token适当做一些调整
    // 自行查看源码,其实就是正则匹配到的内容的一个预处理
    preFilters = Expr.preFilter;

    // 递归检测字符串
    // 比如"div > p + .cl input[type="checkbox"]"
    while ( soFar ) {

      //  以第一个逗号切割选择符,然后去掉前面的部分,处理同时传入多个同级选择器的情况,例如:$( "div, span" )
      if ( !matched || (match = rcomma.exec( soFar )) ) {
        if ( match ) {
          // 如果匹配到逗号,将soFar中匹配到的部分删除掉
          soFar = soFar.slice( match[0].length ) || soFar;
        }
        // 往规则组里边压入一个Token序列,目前Token序列还是空的
        groups.push( tokens = [] );
      }

      // 将matched重置为false,为下次判断soFar中是否有内容做准备

      matched = false;

      // 将刚才前面的部分以关系选择器再进行划分
      // 先处理这几个特殊的Token : >, +, 空格, ~
      // 因为他们比较简单,并且是单字符的
      if ( (match = rcombinators.exec( soFar )) ) {
        // 获取到匹配的字符
        matched = match.shift();
        // 放入Token序列中
        tokens.push({
          value: matched,
          type: match[0].replace( rtrim, " " )
        });
        // 剩余还未分析的字符串需要减去这段已经分析过的
        soFar = soFar.slice( matched.length );
      }

      // 这里开始分析这几种Token : TAG, ID, CLASS, ATTR, CHILD, PSEUDO, NAME
      // 将每个选择器组依次用ID,TAG,CLASS,ATTR,CHILD,PSEUDO这些正则进行匹配
      // Expr.filter里边对应地 就有这些key
      //如果通过正则匹配到了Token格式:match = matchExpr[ type ].exec( soFar )
      //然后看看需不需要预处理:!preFilters[ type ]
      //如果需要 ,那么通过预处理器将匹配到的处理一下 : match = preFilters[ type ]( match )

      for ( type in Expr.filter ) {

        if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
          (match = preFilters[ type ]( match ))) ) {
          matched = match.shift();
          //放入Token序列中
          tokens.push({
            value: matched,
            type: type,
            matches: match
        });
        //剩余还未分析的字符串需要减去这段已经分析过的
        soFar = soFar.slice( matched.length );
      }
    }

    //如果到了这里都还没matched到,那么说明这个选择器在这里有错误
    //直接中断词法分析过程
    //这就是Sizzle对词法分析的异常处理
    if ( !matched ) {
      break;
    }
  }
  //放到tokenCache函数里进行缓存
  //如果只需要这个接口检查选择器的合法性,直接就返回soFar的剩余长度,倘若是大于零,说明选择器不合法
  //其余情况,如果soFar长度大于零,抛出异常;否则把groups记录在cache里边并返回,
  return parseOnly ?
    soFar.length :
    soFar ?
    Sizzle.error( selector ) :
    tokenCache( selector, groups ).slice( 0 );
}

jQuery Sizzle选择器(一)的更多相关文章

  1. [转]JQuery - Sizzle选择器引擎原理分析

    原文: https://segmentfault.com/a/1190000003933990 ---------------------------------------------------- ...

  2. jQuery Sizzle选择器(二)

    自己开始尝试读Sizzle源码.   1.Sizzle同过自执行函数的方式为自己创建了一个独立的作用域,它可以不依赖于jQuery的大环境而独立存在.因此它可以被应用到其它js库中.实现如下:(fun ...

  3. jQuery Sizzle选择器(三)

    在Sizzle的入口方法Sizzle()中看到的一个根据浏览器来初始化document各个方法的函数setDocument(),接下来主要看一下这个方法都做了什么. 但之前有必要看一下它用到的一些Si ...

  4. jQuery源码分析系列(三)Sizzle选择器引擎-下

    选择函数:select() 看到select()函数,if(match.length === 1){}存在的意义是尽量简化执行步骤,避免compile()函数的调用. 简化操作同样根据tokenize ...

  5. jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——编译原理

    这一节要分析的东东比较复杂,篇幅会比较大,也不知道我描述后能不能让人看明白.这部分的源码我第一次看的时候也比较吃力,现在重头看一遍,再分析一遍,看能否查缺补漏. 看这一部分的源码需要有一个完整的概念后 ...

  6. JQuery Sizzle引擎源代码分析

    最近在拜读艾伦在慕课网上写的JQuery课程,感觉在国内对JQuery代码分析透彻的人没几个能比得过艾伦.有没有吹牛?是不是我说大话了? 什么是Sizzle引擎? 我们经常使用JQuery的选择器查询 ...

  7. Sizzle选择器引擎介绍

    一.前言 Sizzle原来是jQuery里面的选择器引擎,后来逐渐独立出来,成为一个独立的模块,可以自由地引入到其他类库中.我曾经将其作为YUI3里面的一个module,用起来畅通无阻,没有任何障碍. ...

  8. JavaScipt 源码解析 Sizzle选择器

    jQuery的定位就是一个DOM的操作库,那么可想而知选择器是一个至关重要的模块.Sizzle,作为一个独立全新的选择器引擎,出现在jQuery 1.3版本之后,并被John Resig作为一个开源的 ...

  9. 关于jquery ID选择器的一点看法

    最近看到一道前端面试题: 请优化selector写法:$(".foo div#bar:eq(0)") 我给出的答案会是: 1. $("#bar") 2.  $( ...

随机推荐

  1. 转:关于VS2012连接MySql数据库时无法选择数据源

    原文来自 http://www.cnblogs.com/sanduo8899/p/3698617.html 您的C#开发工具是用VS2012吗?     No! return;     您的数据库用的 ...

  2. mongodb 学习1

    基本概念 MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.它在许多场景下可用于替代传统的关系型数据库或键/值存储方式( 文件存储格式为BSON(一种JS ...

  3. Remote SSH: Using JSCH with Expect4j

    Now-a-days, we can see that whole world is moving around Clouds and virtualization. More and more ap ...

  4. perl 模块的创建以及制定perl 模块的路径

    1) perl 模块的创建 perl 模块的后缀名为.pm, 其中的内容和一般的perl脚本相同, perl模块中通常放置可重用的函数以及变量, 比如创建一个fasta.pm,里面包含一个统计fast ...

  5. 允许root远程登录Solaris

    注释掉:/etc/default/login文件中CONSOLE=/dev/console

  6. Java写 插入 选择 冒泡 快排

    /** * Created by wushuang on 2014/11/19. */ public class SortTest { @Test public void mainTest() { i ...

  7. 在Android平台下搭建PhoneGap开发环境--用HTML5开发游戏

    一.在Android平台下搭建PhoneGap开发环境具体怎么搭建我这里就不详细说了,如有需要我后面再讲 . PhoneGap 官方地址有详细说明:http://www.phonegap.com. 在 ...

  8. [Learn AF3]第七章 App framework组件之Popup

    AF3的弹出对话框Popup 组件名称:Popup 是否js控件:是,$.afui.popup     说明:af3中的popup和af2中相比变化不大,依然是通过插件popup来实现的     方法 ...

  9. EA修改生成代码的表头注释

    我们在做项目的过程中,每个代码文件都应有此文件的注释,比如说作者,文件说明等.但是如果用EA生成的代码文件的注释是纯英文的,而且有些不是我们需要显示的注释,有些我们需要显示的它又不具备.那么我们就可以 ...

  10. [转]Linux内核源码详解--iostat

    Linux内核源码详解——命令篇之iostat 转自:http://www.cnblogs.com/york-hust/p/4846497.html 本文主要分析了Linux的iostat命令的源码, ...