本文地址:http://www.cnblogs.com/xesam/archive/2012/02/15/2352466.html
Sizzle引擎--原理与实践(一)
大家都知道,Sizzle是jQuery的御用选择器引擎,是jQuery作者John Resig写的DOM选择器引擎,速度号称业界第一。另外,Sizzle是独立的一部分,不依赖任何库,如果你不想用jQuery,可 以只用Sizzle。所以单独拿出来特别对待。在Prototype1.7中,选择器也采用了Sizzle,不过版本有点老,所以我去Sizzle网站搞了一份新的 下来,于是下面分析的时候使用的是最新版的Sizzle.js
预先说明
在分析初期为了保证各个浏览器的结果一致,不考虑原生getElementsByClass以及querySelectorAll的影响,同时忽略XML类型,因此作一下处理:
源码1292行,
(function(){
...
})()
改为:
(function(){ return false; ... })()
源码1140行
if ( document.querySelectorAll ) {
...
}
改为:
if ( document.querySelectorAll && false) { ... }
至于其他的浏览器兼容处理部分,会在初步分析中一并涉及。
预备知识
CSS选择器,jQuery选择器。由于jQuery选择器的形式来自CSS,但是在CSS的基础上又增加了很多新的选择表达式,因此,一切以jQuery选择器为基础。
实例开题:
对于一个选择表达式:
div#outer > ul , div p:nth(1),form input[type="text"]
关于分块的讨论
这里面包含三个并联的选择符,我们怎么处理?
解决方案:
1、可以用split(',')来处理,但是这样只是单纯的分割出来了,并不能获得更多的信息。
2、所以我们采用正则来分块,缺点是可读性以及效率的问题,优点是可以提取一些必要的信息,进行预处理。
所以jquery采取的是正则,但是并没有完全分块,而是一部分一部分的取。对于上面的例子我们看看怎么分块。
div#outer > ul , div p:nth(1),form input[type="text"]先分离出来div#outer > ul,处理完毕后再分离出来div p:nth(1),处理完毕后再分离出来form input[type="text"],最后合并三部分的结果
关于选择顺序的讨论
这里需要记得jQuery选择器的作用
一个典型的例子:
body div p:first-child body div p:first
这两个的含义完全不一样,如果可以用括号这么写的话,可以改成:
body div p:first-child --> body div (p:first-child) body div p:first --> (body div p):first
first-child是用来限定p的,可以算是p的一个属性,
body div p:first是用来限定body div p结果集的,可以算是body div p结果集的一个方法。
body div p:first == (body div p).eq(0)
类似的情况还有jQuery自定义的几个位置查询表达式,所以
情况一、body div p:first
自左向右的查询。先找到body,获得集合A,然后在集合A中查找div获得集合B,在集合B中查找p获得集合C,最后取集合C的第一个元素,得最终结果XX
情况二、body div p:first-child
自右向左的查询,先找到p:first-child获得集合A1,然后判断祖先元素里面是否有div获得集合B1,然后判断祖先元素里面是否有body获得集合C1,得最终结果C1
对比上面的两个过程,相较于过程一,过程二更像是一个过滤的过程,因此,Sizzle最大的亮点是自右向左过滤。
另外,为了提高查询效率,最重要的就是缩小查找范围和减少遍历次数。
一个典型的例子是:
div p
情况一、先找到p,获得集合A,然后判断祖先元素里面是否有div获得集合B,得最终结果B
div#a p
#a一般只会有一个,所以情况一里面p的范围太大了,所以如果第一个选择表达式里面含有id,Sizzle总是先查找第一个,从而缩小查找范围
情况二、先找到div#a,获得单个元素A1,然后再A1的环境中查找p,得最终结果B1
因此,最终的过程就变成
第一、分割表达式
第二、查找元素
第三、过滤元素(过滤分两种,1、通过元素自身属性过滤 2、通过元素之间关系过滤。)
因此可以获得Sizzle的大致代码结构:
Sizzle引擎基本结构
主要流程:window.Sizzle = function(){};
查找元素:Sizzle.find = function(){};
过滤元素:Sizzle.filter = function(){};
定义用到的一些查找方式和过滤条件:Sizzle.selectors = {};
Sizzle.selectors经常要用到,于是给它一个简短的名字,就叫做Expr
于是Sizzle的代码框架如下:
window.Sizzle = function(){ //主要负责分块和主线流程,通过元素之间关系过滤也被放在这个部分 }; Sizzle.find = function(){ //查找元素 }; Sizzle.filter = function(){ //过滤元素,主要处理通过元素自身属性过滤 }; Sizzle.selectors = { //命名空间,以及定义一些变量和特异方法 }; var Expr = Sizzle.selectors;//别称
剩下的问题就是怎么获得我们需要的元素集合
关于查找元素的初步讨论
我们先看,怎么找元素,浏览器原生只有三种查找元素的方式
(文章开头我们已经假设初步所有的浏览器原生都不支持getElementsByClassName,虽然大部分都支持):
getElementById、getElementsByName以及getElementsByTagName。
问题一:
当我们遇到一个选择表达式的时候,怎么判断这个选择表达式是什么类型的。
解决方案:
依次检测这个表达式的类型,获得匹配的类型,注意,一旦获得了匹配的类型,就不会继续匹配了。
至于先匹配哪个类型,查找原则就是尽可能的缩小检索范围(特异性越高,检索范围就越小)。一般情况下,ID数量小于NAME数量,NAME数量又小于TAG数量。因此判断顺序就是['ID','NAME','TAG']
实例说明:
1、input[name="test"],先查找[name="test"],此时虽然还可以继续查找input,但是没有那个必要了,因为如果查询input之后再去取交集,会破坏程序的结构。
Sizzle的处理方式就是:先查找[name="test"]获得集合A,然后在A中过滤input,input被作为一个过滤条件丢到过滤部分去处理。
2、input#demo[name="test"] 由于ID的优先级比NAME高,所以先查找#demo获得集合A,然后在A中过滤input[name="test"],input[name="test"]被作为一个过滤条件丢到过滤部分去处理。
关于过滤元素的初步讨论
过滤是一个条件一个条件来进行的,对于一个集合A以及过滤表达式
expr='[class="test"][rel=1]:first-child'
在A中过滤[class="test"]得到集合B,此时过滤表达式expr='[rel=1]:first-child'
在B中过滤[rel=1]得到集合C,此时过滤表达式expr=':first-child'
在C中过滤:first-child得到集合D,此时过滤表达式expr='',过滤完毕
【说明,上面的顺序不代表真实的顺序,是指为了说明“过滤是一个条件一个条件来进行的”这句活而已】
过滤分两步:
第一、预过滤Expr.preFilter,处理兼容问题以及格式转换,决定下一步的走向
第一、最终过滤Expr.filter,这一步的形式都是一样的,最终返回的都是一个布尔值。
过滤方式:
假设第一轮的筛选集合:A = ['#1','#2','#3','#4','#5','#6']
过滤条件为isMatch
满足条件的集合为C = ['#1','#2','#3']
第一、
var B = []; for( i = 0; (item = A[i]) != null; i++ ){ A[i] = isMatch(item); } for( i = 0; i < A.length; i++ ){ if(A[i]){ B.push(A[i]); } }
1、['#1','#2','#3','#4','#5','#6']
2、['#1','#2','#3',false,false,false]
3、['#1','#2','#3']
第二、
var B = []; for( i = 0; (item = A[i]) != null; i++ ){ if(isMatch(item)){ B.push(A[i]); } }
相关推荐
资源分类:Python库 所属语言:Python 资源全名:ws_sizzle-0.0.4-py3-none-any.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
Sizzle 开发环境 一个 /g/ 用户将 OpenNT NTOSBE 构建环境移植到 Win2003 源,作为源中包含的 razzle 构建环境的替代品。 目前它可以很好地构建源代码,但用户注意到了...上次指南更新的最新版本: Sizzle-devtest.7z
python库,解压后可用。 资源全名:sphinx_sizzle_theme-0.0.8-py2.py3-none-any.whl
资源来自pypi官网。 资源全名:sphinx_sizzle_theme-0.0.8-py2.py3-none-any.whl
在 http://www.cnblogs.com/strick/p/5078435.html 有描述
Sizzle: A Standards-based end-to-end Security ArchitectureSizzle: A Standards-based end-to-end Security ArchitectureSizzle: A Standards-based end-to-end Security Architecture
在分析Sizzle源码之前,先整理一下选择器的工作原理,先明确一些选择器中用到的名词,后边阅读时不会有歧义
前端项目-sizzle,一个纯粹的JavaScript、自下而上的CSS选择器引擎,设计为可以轻松地放到主机库中。
sizzle-landing-page:菜品审查应用程序的纯HTML登陆页面
也许安装 ruby 的更好方法是通过 如果您还没有安装 在您的项目根目录中运行: $ 捆绑安装用法安装generator-sizzle-jekyll : npm install -g generator-sizzle-jekyll创建一个新目录,然后cd进入: mkdir my-...
在 http://www.cnblogs.com/strick/p/5078435.html 描述
于是看了jquery的源码,jquery用的选择器的引擎是sizzle,是jquery的作者另一开源项目,在github上面有,号称最快的dom选择器!不到2000行代码。上面说了不是很精彩的开场白,我么来个 for example: $(‘.test’) 在...
Windows物联网开发开始风靡一时–了解通用Windows平台 “ Windows物联网开发的热潮–学习通用Windows平台”演示文稿的源代码 随时向我发的评论,反馈或问题。
jQuery / Sizzle自定义伪选择器 只是我的伪装收藏。 表现 请注意将通过这些选择器传递多少个元素,因为它们将无法利用本机DOM querySelectorAll()方法提供的性能提升。 为了在使用这些伪指令中的任何一个时获得最佳...
NULL 博文链接:https://wubinclat.iteye.com/blog/471850
Sizzle 是一套前端用智能来处理模块和组件依赖关系的框架。 它遵循 CommonJS 的运行规范来完成对各个模块的动态加载。而且可以同时包含其他库而不产生冲突。 Sizzle的使用很简单,其下共有 5 个常用的方法, 只...
Sizzle,是jQuery作者John Resig写的DOM选择器引擎,速度号称业界第一。作为一个独立全新的选择器引擎,出现在jQuery 1.3版本之后,并被John Resig作为一个开源的项目。Sizzle是独立的一部分,不依赖任何库,如果你...