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. java-信息安全(一)-BASE64,MD5,SHA,HMAC,RIPEMD算法

    概述 信息安全基本概念: BASE64 编码格式 Base58 编码 MD5(Message Digest algorithm 5,信息摘要算法) SHA(Secure Hash Algorithm, ...

  2. android中YUV转RGB的方法

    在一个外国网站上看到一段YUV转RGB的程序很不错,根据维基上的知识,方法应该是没问题的,自己也用过了,效果没问题. 首先说一下android上preview中每一帧的信息都是YUV420的,或者叫N ...

  3. UNIX环境编程学习笔记(10)——文件I/O之硬链接和符号链接

    lienhua342014-09-15 1 文件系统数据结构 UNIX 文件系统通过 i 节点来存储文件的信息.如图 1 所示为一个磁盘柱面上的 i 节点和数据块示意图.其中 i 节点是一个固定长度的 ...

  4. 网络上可供测试的Web Service

    网络上可供测试的Web Service 腾讯QQ在线状态 WEB 服务Endpoint: http://www.webxml.com.cn/webservices/qqOnlineWebService ...

  5. nagios安装check_linux_stats.pl插件报错Can't locate Sys/Statistics/Linux.pm in @INC的处理?

    问题描述: 今天想有没有监控主机内存的插件可以供nagios来使用,然后找到一个插件check_linux_stats.pl 但是在将脚本上传之后,执行的时候报错 [root@testvm02 lib ...

  6. go语言之PLAN9汇编

    http://blog.studygolang.com/2013/05/asm_and_plan9_asm/ https://lrita.github.io/2017/12/12/golang-asm ...

  7. ubuntu alsa

    今天要在linux下搞音频编程,在网上查阅了一下资料,网上很多资料都是在linux下直接对/dev/dsp进行编程的,因为在以往的linux系统中,我们是可以通过cat  xxx.wav /dev/d ...

  8. C# 调用dephi dll 实例

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Runti ...

  9. IOS端的摇一摇功能

    //微信的摇一摇是怎么实现的~发现原来 ios本身就支持 //在 UIResponder中存在这么一套方法 - (void)motionBegan:(UIEventSubtype)motion wit ...

  10. js实现点击评论进行显示回复框

    有人在群里问如何在留言评论那里点击回复按钮,下面就自动显示一个回复框,他想要的效果如图: 于是我随意的写了一段HTML,代码如下: <!DOCTYPE HTML> <html lan ...