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. Linux mint 亮度调节

    刚装上的mint亮度严重影响操作,快速调节mint亮度的方法 echo 1000 >/sys/class/backlight/intel_backlight/brightness 1000这个数 ...

  2. CF 1100E Andrew and Taxi(二分答案)

    E. Andrew and Taxi time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  3. Python守护进程和脚本单例运行

    Python 守护进程 守护进程简介 进程运行有时候需要脱离当前运行环境,尤其是Linux和Unix环境中需要脱离Terminal运行,这个时候就要用到守护进程.守护进程可以脱离当前环境要素来执行,这 ...

  4. python requests模块中返回时间elapsed解析

    一.问题: Python 中requests库在发送http请求时相当方便好用,但在使用时一直受一个问题困扰,怎么才能查看请求时长呢? 自己写时间函数再相减?NO,这个方法肯定不行. 二.解决: 好吧 ...

  5. iOS - 集成Bundle资源文件包

    1.Bundle 文件 Bundle 文件,简单理解,就是资源文件包.我们将许多图片.XIB.文本文件组织在一起,打包成一个 Bundle 文件.方便在其他项目中引用包内的资源. Bundle 文件是 ...

  6. sublime--将vue代码进行高亮显示

    vue的.vue文件sublime是不认识,但是为了让 .vue 文件看上去更加简洁:所以要用到一款不错的插件: 下载:vue-syntax-highlight https://gitee.com/m ...

  7. MPD大会北京上海两站圆满落幕

    MPD大会北京上海两站圆满落幕 由麦思博(MSUP)主办的亚太软件研发团队管理峰会(以下简称MPD大会)分别于6月15及6月22日在北京.上海成功举办.国内外知名软件.互联网行业项目领头人及业内从业人 ...

  8. Scala学习笔记(3)-表达式归纳

    语法:使用表达式定义值和变量 val <identifier>[:<type>]=<expression>  字面值类型 var <identifier> ...

  9. HDU 1686 - Oulipo - [KMP模板题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1686 Time Limit: 3000/1000 MS (Java/Others) Memory Li ...

  10. Codeforces Round #433 (Div. 2, based on Olympiad of Metropolises)

    A. Fraction 题目链接:http://codeforces.com/contest/854/problem/A 题目意思:给出一个数n,求两个数a+b=n,且a/b不可约分,如果存在多组满足 ...