Sizzle引擎研究

Sizzle引擎研究原文地址 http foio github io sizzle research 什么是 sizzle 下面时官方的一段解释 Apure javascriptCS Standalone nodependenci Competitivep Only4kBwithg Easyt

原文地址:http://foio.github.io/sizzle-research/

什么是sizzle?下面时官方的一段解释。

A pure-javascript CSS selector engine × Standalone(no dependencies) × Competitive performance × Only 4kB with gzipped × Easy to use × Css3 support × Bla bla …… 

其实说白了,sizzle就是一个很给力的选择器解析引擎。

我们为什么需要sizzle呢?其实对现代浏览器来说,document.querySelectorAll就可以解决一切。比如zeptoJs就是用querySelectorAll进行选择器解析的,因为移动端所有浏览器都支撑querySelectorAll。但是对于低版本的IE(<=8)浏览器,不仅不支持querySelectorAll,连getElementById都有bug,因此自己用浏览器原生API解析选择器简直难上加难。好在sizzle引擎帮我们处理了一切。知其然,更要知其所以然。下面让我们看看sizzle引擎内部时如何实现的。

sizzle解析器的主要有以下几个工作步骤。

Sizzle引擎研究

接下来我们就依次解析。为了简单,我们在接下来的文章中都使用选择器div input[name=ttt]作为例子。

1.词法分析


词法分析是指我们将文本代码解析为一个个记号(token),以便后续语法分析使用。

(1) sizzle的token种类

css选择器的词法分析相对较为简单,不用通过lex等专业工具,简单的正则表达式就搞定了。下面依次是用于切分分组,层级关系,以及单个元素的正则表达式。

分组(,):/^[\x20\t\r\n\f]*,[\x20\t\r\n\f]*/

层级关系( >+~):/^[\x20\t\r\n\f]*([>+~]|[\x20\t\r\n\f])[\x20\t\r\n\f]*/

单个元素处理:
        var characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+"
        var ID = new RegExp("^#(" + characterEncoding + ")")
        var TAG = new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" )
        var Class = new RegExp( "^\\.(" + characterEncoding + ")" )

(2)从左到右扫描生产token集合

用正则表达式切分出token的过程,如下代码所示。基本原理就是从左到右扫描,用正则切分。

//分组
  var rcomma = /^[\x20\t\r\n\f]*,[\x20\t\r\n\f]*/;
  //层级
  var rcombinators =           
 /^[\x20\t\r\n\f]*([>+~]|[\x20\t\r\n\f])[\x20\t\r\n\f]*/
  //选择器
  var TAG = /^((?:\\.|[\w*-]|[^\x00-\xa0])+)/;
  var matchExpr = { 
      
      CLASS: /^\.((?:\\.|[\w-]|[^\x00-\xa0])+)/,
      TAG: /^((?:\\.|[\w*-]|[^\x00-\xa0])+)/
  };
  //扫描
  while (selector) { 
      
      //分组
      if (match = rcomma.exec(selector)) { 
      
          selector = selector.slice(match[0].length)
          groups.push((tokens = []));
      }
      //层级关系
      if ((match = rcombinators.exec(selector))) { 
      
          matched = match.shift();
          tokens.push({ 
      
              value: matched,
              type: match[0].replace(rtrim, " ")
          });
          selector = selector.slice(matched.length);
      }
      //选择器
      for (type in matchExpr) { 
      
          if ((match = matchExpr[type].exec(selector))) { 
      
              matched = match.shift();
              tokens.push({ 
      
                  value: matched,
                  type: type,
                  matches: match
              });
              selector = selector.slice(matched.length);
          }
      }
  }

最终生成的token集合如下:

{ 
      matches: ["div"],type: "TAG",value: "div“ }, {matches:[“”], type: " ", value: " "}, {matches: ["input"], type: "TAG", value: "input"}, {matches: ["name"], type: "ATTR", value: "[name=ttt]"}

2.过滤函数


过滤函数用于从浏览器dom模型中找到基本符合css选择器的种子集,sizzle针对每一种token都实现一个过滤函数,如下代码所示:

//各种类型的token的过滤器,全部返回闭包函数
Expr.filter = { 
       
    ATTR   : function (name, operator, check) { 
       return closure}
    CHILD  : function (type, what, argument, first, last) { 
       return closure}
    CLASS  : function (className) { 
       return closure}
    ID     : function (id) { 
       return closure}
    PSEUDO : function (pseudo, argument) { 
       return closure}
    TAG    : function (nodeNameSelector) { 
        return function(elem) { 
       
    return elem.nodeName && elem.nodeName.toLowerCase() === nodeNameSelector;
              };
     }
}

通过部分过滤函数,我们可以初步得到符合条件的种子集合。如下图

Sizzle引擎研究

3.编译函数

其实sizzle引擎最难的地方就在编译函数。为什么叫做编译呢?抽象的讲,把高级规则转换成底层实现就叫编译;比如高级语言到机器语言的过程就是编译。同样把抽象的css选择语法转变成具体的匹配函数的过程也是编译。

Sizzle引擎研究

编译的过程还是比较复杂的,其实就是从左到右扫描css选择表达式,并使用与当前token对应的过滤函组合成最终的超级匹配函数。扫描编译的核心步骤是:

(1)遇到关系token(+> ~)则依次出栈并根据层级规则合并栈中函数
(2)其他情况将当前token对应的处理函数压入栈中
(3)选择器表达式结束后依次出栈并合并栈中函数

很难说清楚,高手常常说一图胜千言,我也把扫描编译css选择表达式div [name=ttt]的过程做成图,希望能够讲清楚。

Sizzle引擎研究

现在假设我们已经通过编译获得了最终的超级匹配函数。那么从种子集中找到结果集就比较简单了。

for item in seed
      if(superMatcher(item )){ 
          
               resultSet.push(item);
      }
return resultSet;






版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/208453.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午11:33
下一篇 2026年3月19日 上午11:34


相关推荐

  • linux测试snmpwalk是否连通,snmpwalk用法

    linux测试snmpwalk是否连通,snmpwalk用法SNMPWALK 1 Net SNMPSNMPWALK 1 NAMEsnmpwalk communicates SYNOPSISsnmp

    2026年3月19日
    2
  • 【Java基础知识 1】Java入门级概述

    【Java基础知识 1】Java入门级概述1991年成立了一个称为Green的项目小组,帕特里克、詹姆斯·高斯林、麦克·舍林丹和其他几个工程师一起组成的工作小组在加利福尼亚州门洛帕克市沙丘路的一个小工作室里面研究开发新技术,专攻计算机在家电产品上的嵌入式应用。由于C++所具有的优势,该项目组的研究人员首先考虑采用C++来编写程序。但对于硬件资源极其匮乏的单片式系统来说,C++程序过于复杂和庞大。为了解决困难,他们首先着眼于语言的开发,对于新语言的设计,Sun公司研发人员并没有开发一种全新的语言,而是根据嵌入式软件的要求,对C++进行了…

    2025年12月2日
    8
  • 飞机订票系统(C语言)

    飞机订票系统(C语言)用C语言解决飞机订票系统***一、问题描述:根据以下功能说明,设计航班信息、客户信息、订票信息的存储结构,设计程序完成相应功能。*录入:*可以录入航班情况(数据可以存储在一个数据文件中,数据个数不能少于8个、自行设计数据构成);客户信息(姓名,证件号,电话等);订票信息(订单要有编号,其余数据自行设计)。****查询:****可以查询某个航线的情况(如,输入航班号,查询起降时间,起飞抵达城市,航班票价,票价折扣,确定航班是否满仓);可以输入起飞抵达城市,查询飞机航班情况;****订票:**

    2022年6月29日
    28
  • 构建可精准控制的垂直滑块:解决 offsetY 偏移错位与旋转干扰问题

    构建可精准控制的垂直滑块:解决 offsetY 偏移错位与旋转干扰问题

    2026年3月15日
    2
  • Claude Code教程(一) | 认识 Claude Code

    Claude Code教程(一) | 认识 Claude Code

    2026年3月16日
    2
  • UNet详解(附图文和代码实现)

    卷积神经网络被大规模的应用在分类任务中,输出的结果是整个图像的类标签。但是UNet是像素级分类,输出的则是每个像素点的类别,且不同类别的像素会显示不同颜色,UNet常常用在生物医学图像上,而该任务中图片数据往往较少。所以,Ciresan等人训练了一个卷积神经网络,用滑动窗口提供像素的周围区域(patch)作为输入来预测每个像素的类标签。这个网络有两个优点:(1)输出结果可以定位出目标类别的位置;(2)由于输入的训练数据是patches,这样就相当于进行了数据增强,从而解决了生物医学图像数量少的问题。但是,

    2022年4月4日
    227

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号