Sizzle 源码分析 (一)

2.1 稳定 版本

Sizzle 选择器引擎博大精深,下面开始阅读它的源代码,并从中做出标记 。先从入口开始,之后慢慢切入 。

入口函数 Sizzle ()

源码 194-301 行

function Sizzle(selector, context, results, seed) {
var match, elem, m, nodeType,
i, groups, old, nid, newContext, newSelector; //37 行, preferredDoc = window.document,
// 这里防止document被重写。
// 如果了上下文对象的ownerDocument || preferredDoc 不等于document,覆写所有涉及到document的内部方法
// 在这里传递参数 context,即覆写 context.ownerDocument || preferredDoc 的方法 。
// 469-838 setDocument()
// 在setDocument中,如果 context存在,那么就设置覆写 context.ownerDocument ,否则覆写 preferredDoc
if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
setDocument(context);
} context = context || document;
results = results || []; // 如果不是字符串,或者为null或undefined,即立即返回
if (!selector || typeof selector !== "string") {
return results;
}
//如果上下文不是dom元素,document,fragment文档片段之一,那么返回空数组
if ((nodeType = context.nodeType) !== 1 && nodeType !== 9 && nodeType !== 11) {
return [];
}
// documentIsHTML 表示为HTML文档, seed表示种子元素 。
// 如果给出了种子元素,那么直接跳到过滤步骤 不再快速查找
if (documentIsHTML && !seed) { //不能在文档片段下使用快速匹配,因为文档片段不再document节点下, 而是在自己的节点下 ,使用fragmentDocument.getE... 查找.
if (nodeType !== 11 && (match = rquickExpr.exec(selector))) {
// 快速匹配正则表达式 135 行, rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
if ((m = match[1])) {
// id 选择器
if (nodeType === 9) { // 如果上下文是document
elem = context.getElementById(m);
// 防止蓝莓 4.6 版本,还会选择到已经不在document树中的元素 。
if (elem && elem.parentNode) {
// 防止IE,Opera,Webkit 低版本 会返回name 也是这个选择器的元素。
if (elem.id === m) {
results.push(elem);
return results;
}
} else {
// 如果父元素不存在,说明已经不在DOM树中了 。
return results;
}
} else {
// 如果上下文不是document,那么要调用 document的方法,因为普通元素没有getElementById 方法 。因为在前面setDocument已经保证过 context.ownerDocument 一定包含原生document的方法。
if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) &&
contains(context, elem) && elem.id === m) {
// 716 行,contains 方法保证了 elem 一定位于context的dom树内。
results.push(elem);
return results;
}
}
} else if (match[2]) {
// Tag 选择器
// 这里一定要用apply,因为 context.getElementsByTagName(selector) 是一个类数组,通过 apply将其解构 。
push.apply(results, context.getElementsByTagName(selector));
return results; } else if ((m = match[3]) && support.getElementsByClassName) {
// 类选择器, apply原理同上 。
// 521 行检测,support 是做功能检测 。 <IE9 不支持 getElementsByClassName
push.apply(results, context.getElementsByClassName(m));
return results;
}
} // 如果只是 querySelector ,那么直接调用这个接口 rbuggyQSA检测是否存在bug
// 如果 rbuggyQSA 不存在bug,就可以使用
// 如果存在bug,但是选择器中没有包含bug 的部分。
if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
// 随机数
nid = old = expando;
// 上下文
newContext = context;
// querySelectorAll实现存在BUG,它会在包含自己的集合内查找符合自己的元素节点。 但是根据规范,应该是当前上下文的所有子孙下找,
newSelector = nodeType !== 1 && selector;
// 如果上下文是 Element,就要处理 querySelectorAll的 BUG 。
if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") {
groups = tokenize(selector);
// 如果存在ID,则将ID取得出来放到这个分组的最前面,比如div b --> [id=xxx] div b
// 不存在ID,就创建一个ID,重复上面的操作,但最后会删掉此ID // 这里的实现其实是非常巧妙的 。 // 举一个例子,存在选择器 .pawn span 。并且上下文是 document.querySelector(".pawn") 。
// 由于 querySelectorAll的 bug,查找的时候依旧会查找到span,但是根据原则,应当在.pawn 的下面查找,也就是不能允许span 。
// 作者想到一种办法,给 .pawn 再加一个id,比如 :shit123,那么选择器变为 #shit123 .pawn span。
// 但是,#shit123 和 .pawn 是同级的,#shit123 .pawn 选择器就存在矛盾,便无法选择到span。
// 把选择器重组之后,再将父元素向上提一层 。
//这样并不会造成遗漏,因为如果 选择器最高层为context,按照要求无法选中,重组之后又矛盾,自然无法选中 ,如果最高层不是context,那么外面包一层context,再将父元素向上提一层也无妨 。 if ((old = context.getAttribute("id"))) {
//138行, 重新转义字符 rescape = /'|\\/g,
nid = old.replace(rescape, "\\$&");
} else {
// 如果没有id,则设置id 。
context.setAttribute("id", nid);
}
// ID 属性选择器
nid = "[id='" + nid + "'] "; i = groups.length;
while (i--) {
groups[i] = nid + toSelector(groups[i]);
}
// 137 行, rsibling = /[+~]/,
// function testContext(context) {
// return context && typeof context.getElementsByTagName !== "undefined" && context;
// }
// IE8下如果元素节点为Object,无法找到元素 。因此上下文如果是Object节点,那么向上提一层
newContext = rsibling.test(selector) && testContext(context.parentNode) || context;
newSelector = groups.join(",");
}
// 如果上下文是 元素,并且是Object,那么 newSelector 为假,直接跳过此步骤。
// IE<9 在object下获取不到元素 。
if (newSelector) {
try {
push.apply(results,
newContext.querySelectorAll(newSelector)
);
return results;
} catch (qsaError) {
} finally {
if (!old) {
context.removeAttribute("id");
}
}
}
}
} //
// 其他复杂的情况就调用select方法进行选择
// whitespace = "[\\x20\\t\\r\\n\\f]",
//通过replace 去掉两边的空格进行查找 。
// 103行, rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"),
return select(selector.replace(rtrim, "$1"), context, results, seed);
}

Sizzle源码分析 (一)的更多相关文章

  1. sizzle源码分析 (4)sizzle 技术总结及值得我们学习的地方

    分析sizzle源码并不是为了去钻牛角尖,而是去了解它的思想,学习下期中一些技术的运用. 1,sizzle中的正则表达式jquery源码中充斥着各种正则表达式,能否看懂其源码的关键之一就是对正则表达式 ...

  2. Sizzle源码分析:一 设计思路

    一.前言 DOM选择器(Sizzle)是jQuery框架中非常重要的一部分,在H5还没有流行起来的时候,jQuery为我们提供了一个简洁,方便,高效的DOM操作模式,成为那个时代的经典.虽然现在Vue ...

  3. Sizzle源码分析:三 筛选和编译

    好了有了之前的词法分析过程,现在我们来到select函数来,这个函数的整体流程,前面也大概说过: 1. 先做词法分析获得token列表 2. 如果有种子集合直接到编译过程 3. 如果没有种子集合并且是 ...

  4. Sizzle 源码分析 (二)

    在Sizzle函数中,如果能快速处理或者通过querySelector处理,那么就使用它处理.否则使用select函数处理 . select函数 select = Sizzle.select = fu ...

  5. Sizzle源码分析:二 词法分析

    上一篇我们了解了Sizzle的整体流程,下面我开始一点点分析各个流程,我们进行查询的第一步就是词法分析tokenize,同样先了解下思路,如果是#div_test > span input[ch ...

  6. sizzle源码分析 (3)sizzle 不能快速匹配时 选择器流程

    如果快速匹配不成功,则会进入sizzle自己的解析顺序,主要流程如下: 总结流程如下: (1)函数sizzle是sizzle的入口,如果能querySelectAll快速匹配,则返回结果 (2)函数S ...

  7. sizzle源码分析 (2)ID 类 tag querySelectorAll 快速匹配

    不是所有的选择器都需要去分词,生成相应的匹配函数,这样流程比较复杂,当浏览器具备原生的方法去匹配元素是,没有理由不优先匹配,下面看看进入Sizzle后,它是怎么优先匹配这些元素的: function ...

  8. sizzle源码分析 (1)sizzle架构

    sizzle是jquery的核心,它用来选择匹配的元素,其代码包含在一个匿名函数中,并以window作为其上下文环境: (function( window, undefined ) { //此处为si ...

  9. jQuery 2.0.3 源码分析Sizzle引擎 - 高效查询

    为什么Sizzle很高效? 首先,从处理流程上理解,它总是先使用最高效的原生方法来做处理 HTML文档一共有这么四个API: getElementById 上下文只能是HTML文档 浏览器支持情况:I ...

随机推荐

  1. explain 和 desc 详解

    MySQL性能分析 1.使用explain语句去查看分析结果 如explain select * from test1 where id=1;会出现:id  selecttype  table  ty ...

  2. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十七:IIC储存模块 - FIFO读写

    . int main() . { . int A: . A = : . } 代码17.1 话题为进入之前,首先让我们来聊聊一些题外话.那些学过软核NIOS的朋友可曾记得,软核NIOS可利用片上内存作为 ...

  3. dhroid - NetJSONAdapter 网络化的adapter

    关于adapter 我想对于大家来说已经不陌生了,基本应用都会用的很多,不知道现在你是不是还是按一定的套路写很多代码去实现adapter我想大多数人还是写个adapter继承自baseadapter ...

  4. Unity3D笔记十八 GL图像库

    1.绘制2D图像的时需要使用GL.LoadOrtho()方法来将图形映射到平面中. 2.所有绘制相关的内容都要写在OnPostRender()方法中. 3.有关GL图像库的脚本需要绑定到Hierarc ...

  5. html如何让label在div中的垂直方向居中显示?

    设置label的行高 line-height 和div的高度一致即可.

  6. iptables黑/白名单设置(使用ipset 工具)

    ipset介绍 ipset是iptables的扩展,它允许你创建 匹配整个地址集合的规则.而不像普通的iptables链只能单IP匹配, ip集合存储在带索引的数据结构中,这种结构即时集合比较大也可以 ...

  7. Elasticsearch 不同的搜索类型之间的区别

    1.match   轻量级搜索 GET /wymlib/_search { "query": { "match": {  "title": ...

  8. php curl常见错误:SSL错误、bool(false)

    症状:php curl调用https出错 排查方法:在命令行中使用curl调用试试. 原因:服务器所在机房无法验证SSL证书. 解决办法:跳过SSL证书检查. curl_setopt($ch, CUR ...

  9. 二步实现 远程连接 阿里云SqlServer 2012 数据库服务器

    前言:在使用 阿里云 上的一些产品时,遇到不少坑. 安装IIS 时,遇到 因买的配置过低,虚拟内存不足,而导致 IIS 总是安装失败: 现在 在上面安装了个 Sql Sever 2012,远程老是 不 ...

  10. 被included或者被required的文件都来自哪里呢

    过PHP,你可以使用不同函数帮助你重用代码.具体用到的函数取决于你打算重用的内容. 主函数如下: * include() and include_once() * require() and requ ...