2.1 稳定 版本
Sizzle 选择器引擎博大精深,下面开始阅读它的源代码,并从中作出标记 。先从入口开始,以后慢慢切入 。javascript
源码 194-301 行java
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); }