看完了上篇,对Sizzle有了一个大致的了解,我们接下来就可以正式开始啃Sizzle的源码了。上来就讲matcher难度太大,先来点开胃菜,讲讲Sizzle中的各个正则表达式的作用吧(本来还想讲初始化的,篇幅太长了,留待下篇吧)。

友情提醒:阅读本文请先学习正则表达式,至少对捕获组以及js的正则API(exec,match,test,字符串转正则)有一定的了解

这是前面一堆变量声明和函数声明。

var Sizzle =
/*!
* Sizzle CSS Selector Engine v1.10.16
* http://sizzlejs.com/
*
* Copyright 2013 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-01-13
*/
(function( window ) {
var i,
support, //后面会初始化为对象,保存支持性
Expr,
getText,
isXML, //是否是XML
compile,
outermostContext, //最大查找范围
sortInput,
hasDuplicate, //刚检查完的两个元素是否重复 // Local document vars
setDocument,
document,
docElem,
documentIsHTML,
rbuggyQSA,
rbuggyMatches,
matches,
contains, // Instance-specific data
//用来对特殊的函数进行标记
expando = "sizzle" + -(new Date()),
//倾向于使用的文档节点
preferredDoc = window.document,
dirruns = 0,
done = 0,
//这里几个cache实际上是函数
//通过classCache(key,value)的形式进行存储
//通过classCache[key+' ']来进行获取
classCache = createCache(),
tokenCache = createCache(),
compilerCache = createCache(),
sortOrder = function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
}
return 0;
},

到这里先停一下,注意到变量声明的时候声明了三个缓存(cache),涉及到createCache这个工具函数,这个工具函数的作用简单来说就是创建一个缓存,用OO思想我们创建的缓存都是声明一个对象,然后再给其加上set、get、delete等方法,但Sizzle不是这样做的,我们先来看看源码。

/**
* Create key-value caches of limited size
* @returns {Function(string, Object)} Returns the Object data after storing it on itself with
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
* deleting the oldest entry
*/
function createCache() {
//用来保存已经存储过的key,这是一种闭包
var keys = [];
//这里使用cache这个函数本身来当作存放数据的对象。
function cache( key, value ) {
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
//key后面加空格是为了避免覆盖原生属性
//当长度超过限制时,则需要删除以前的缓存
if ( keys.push( key + " " ) > Expr.cacheLength ) {
// Only keep the most recent entries
delete cache[ keys.shift() ];
}
//返回存储好的信息
return (cache[ key + " " ] = value);
}
return cache;
}

这里调用createCache返回了一个函数,函数像对象一样可以来以key-value的形式存储信息(jQuery的大部分工具函数都是存储在jQuery这个工厂函数上,这样省了一个命名空间),当直接调用这个返回的函数时,就可以存储信息(set),用cache[key+' ']的形式就可以取出存放的信息(get),之所以要在key后面加空格,是要避免覆盖其他原生属性。

继续返回上面,开始看各种变量声明。

这里的各个变量保存了一些如push之类的原生方法引用,因为这些方法要多次使用,这样的做法可以优化性能。

        // General-purpose constants
//还是不明白这里为什么要把undefined转换为字符串用以判断
strundefined = typeof undefined,
MAX_NEGATIVE = 1 << 31, // Instance methods
hasOwn = ({}).hasOwnProperty,
arr = [],
pop = arr.pop,
push_native = arr.push,
push = arr.push,
slice = arr.slice,
// Use a stripped-down indexOf if we can't use a native one
//先检查一下有没有原生API
indexOf = arr.indexOf || function( elem ) {
var i = 0,
len = this.length;
for ( ; i < len; i++ ) {
if ( this[i] === elem ) {
return i;
}
}
return -1;
},
//用来在做属性选择的时候进行判断
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
//空白符的几种类型
//字符串里的两根斜杠其中有一根用来转义,比如\\x20到了正则表达式里就是\x20
whitespace = "[\\x20\\t\\r\\n\\f]",

然后我们接触到了第二个正则表达式(虽然它现在是字符串,但最终是会传入new RegExp方法的)

这里总结了符合标准的种种空白字符,包括空格、tab、回车、换行等,这里之所以有两根反斜杠(backslash),是因为反斜杠在字符串中是转义符,为了表示反斜杠这一含义,需要用\\这样的写法。

我们继续看后面的正则表达式。

        // http://www.w3.org/TR/css3-syntax/#characters
//\\\\.转换到正则表达式中就是\\.+用来兼容带斜杠的css
//三种匹配字符的方式:\\.+,[\w-]+,大于\xa0的字符+,为什么匹配这三个请看上面的链接
characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Loosely modeled on CSS identifier characters
// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
//这里多了一个#,暂时没细看标准不明白为何
identifier = characterEncoding.replace( "w", "w#" ), // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
//\[ [\x20\t\r\n\f]* ((?:\\.|[\w-]|[^\x00-\xa0])+) [\x20\t\r\n\f]* (?:([*^$|!~]?=) [\x20\t\r\n\f]* (?:['"]) ((?:\\.|[^\\])*?) \3 | () |)|)
//\3 is (['\"])
//这种正则的链接方式对有大量空白的处理非常好,很容易读。
//捕获组序列:
//$1:attrName,$2:([*^$|!~]?=),$3:(['\"]),$4:((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|),$5:(" + identifier + ")
//$1捕获的是attrName,$2捕获的是等号或^=这样的等号方式,$3捕获单双引号
//$4提供三种匹配字符串的方式:\\.*?\3,非斜杠*?\3(因为斜杠没意义),识别符,此处相当于捕获attrValue,只不过要兼容带引号和不带两种形式
//$5捕获识别符
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",

上面两个正则表达式不再赘述,第三个正则表达式先看开头和结尾匹配的是代表属性选择符的'['和']',捕获出来的结果分别代表的含义是[attrName、等号、引号、attrValue、attrValue]。

再看下一个正则表达式

        // Prefer arguments quoted,
// then not containing pseudos/brackets,
// then attribute selectors/non-parenthetical expressions,
// then anything else
// These preferences are here to reduce the number of selectors
// needing tokenize in the PSEUDO preFilter
//$1:pseudoName,$2:((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)
// ,$3:(['\"]),$4:((?:\\\\.|[^\\\\])*?),$5:((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)
//$1捕获伪元素或伪类的名字,$2捕获两种类型的字符,一种是带引号的字符串,一种是attributes那样的键值对。
//$3捕获引号,$4和$5分别捕获$2中的一部分
pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
//$1:((?:^|[^\\\\])(?:\\\\.)*)
//这个用来去除selector多余的空格,免得干扰到后面空格的匹配关系
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
//这个后面用来清除css规则中组与组之间的逗号。
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
//??????第二个whitespace有什么用???
//这个现在可以解答了。。因为空格也是用来连接祖先后代关系中的一种。。
//$1:([>+~]|whitespace)分别捕获4种连接符:'>','+','~','whitespace'
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
//$1:([^\\]'\"]*?)
//??????这个我忘了有什么用。。
rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ),
ridentifier = new RegExp( "^" + identifier + "$" ), //这里是最后用来检测的正则表达式,使用形式通常是matchExpr[tokens[i].type].test(...)
matchExpr = {
"ID": new RegExp( "^#(" + characterEncoding + ")" ),
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
"ATTR": new RegExp( "^" + attributes ),
"PSEUDO": new RegExp( "^" + pseudos ),
//$1:(only|first|last|nth|nth-last),$2:(child|of-type),
// $3:(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))
// $4:(([+-]|)(\\d*)n|),$5:([+-]|),$6:(\\d*),$7:([+-]|),$8:(\\d+)
//这是后面用来检查你是否用到子选择器的。
//$3第一部分匹配四种字符,even,odd,[+-](\d*)n,任意字符。空格,再匹配第二部分:([+-]或任意字符)+空格+(一或多个数字)
//为什么这么做,则要看Sizzle支持的子选择符语法。
//这些捕获组的含义在后面会提到,请结合具体用法理解
"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
// We use this for POS matching in `select`
//$1是(even|odd|eq|gt|lt|nth|first|last),$2是((?:-\\d)?\\d*)
//当选择符匹配这个成功的时候,则说明这个选择符使用的时候需要上下文,而不是像id,tag,class一样直接查找
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
}, rinputs = /^(?:input|select|textarea|button)$/i,
//h1,h2,h3......
rheader = /^h\d$/i,
//用来检测某个API是否是原生API
//例如document.createElement.toString() 的运行结果是 "function createElement() { [native code] }"
rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors
//容易判断或获取的元素单独拿出来,ID,TAG和CLASS,这三个基本有原生API
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/,
rescape = /'|\\/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
//$1:([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.),$2:(" + whitespace + ")
runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
//??????函数用途不明
//卧槽,jQuery还考虑了编码http://zh.wikipedia.org/wiki/UTF-16
//转换为UTF-16编码,若某个字符是多种字符,超过BMP的计数范围0xFFFF,则必须将其编码成小于0x10000的形式。
funescape = function( _, escaped, escapedWhitespace ) {
var high = "0x" + escaped - 0x10000;
// NaN means non-codepoint
// Support: Firefox
// Workaround erroneous numeric interpretation of +"0x"
//这里的high !== 用于判断 high是否是NaN,NaN !== NaN
//当high为NaN,escapedWhitespace 为undefined时,再判断high是否为负数
return high !== high || escapedWhitespace ?
escaped :
high < 0 ?
// BMP codepoint
String.fromCharCode( high + 0x10000 ) :
// Supplemental Plane codepoint (surrogate pair)
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
};

因为写了很多注释,我觉得应该能看懂了。。看不懂的再留言交流好了

jQuery1.11源码分析(2)-----Sizzle源码中的正则表达式[原创]的更多相关文章

  1. jQuery1.11源码分析(1)-----Sizzle源码概览[原创]

    最近在啃jQuery1.11源码,上来就遇到Sizzle这个jQuery的大核心,虽然已经清楚了Sizzle的用途,先绕过去也没事,但明知山有虎偏向虎山行才是我们要做的. 本文面向的阅读对象:正在学习 ...

  2. jQuery1.11源码分析(3)-----Sizzle源码中的浏览器兼容性检测和处理[原创]

    上一章讲了正则表达式,这一章继续我们的前菜,浏览器兼容性处理. 先介绍一个简单的沙盒测试函数. /** * Support testing using an element * @param {Fun ...

  3. NIO 源码分析(05) Channel 源码分析

    目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...

  4. NIO 源码分析(02-2) BIO 源码分析 Socket

    目录 一.BIO 最简使用姿势 二.connect 方法 2.1 Socket.connect 方法 2.2 AbstractPlainSocketImpl.connect 方法 2.3 DualSt ...

  5. NIO 源码分析(02-1) BIO 源码分析

    目录 一.BIO 最简使用姿势 二.ServerSocket 源码分析 2.1 相关类图 2.2 主要属性 2.3 构造函数 2.4 bind 方法 2.5 accept 方法 2.6 总结 NIO ...

  6. [源码分析] 从实例和源码入手看 Flink 之广播 Broadcast

    [源码分析] 从实例和源码入手看 Flink 之广播 Broadcast 0x00 摘要 本文将通过源码分析和实例讲解,带领大家熟悉Flink的广播变量机制. 0x01 业务需求 1. 场景需求 对黑 ...

  7. drf的基本使用、APIView源码分析和CBV源码拓展

    cbv源码拓展 扩展,如果我在Book视图类中重写dispatch方法 -可以实现,在get,post方法执行之前或者之后执行代码,完成类似装饰器的效果 def dispatch(self, requ ...

  8. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  9. k8s client-go源码分析 informer源码分析(3)-Reflector源码分析

    k8s client-go源码分析 informer源码分析(3)-Reflector源码分析 1.Reflector概述 Reflector从kube-apiserver中list&watc ...

随机推荐

  1. 利用matlab写一个简单的拉普拉斯变换提取图像边缘

    可以证明,最简单的各向同性微分算子是拉普拉斯算子.一个二维图像函数 f(x,y) 的拉普拉斯算子定义为 ​ 其中,在 x 方向可近似为 ​ 同理,在 y 方向上可近似为 ​ 于是 我们得到满足以上三个 ...

  2. proe工程图输出dwg/dxf文件设置

    网上看到不少人分享proe转转dxf/dwg配置文件的,但是看了一圈,几乎都没有涉及到转化线型的,所以自己整理自己的配置文件,写在这里分享出来. 以Pro/engineer WF5.0为例: 1.复制 ...

  3. Notes of Daily Scrum Meeting(12.18)

    前期落下的进度我们会在周六周日赶一下,在编译课程设计中期测试之后集中处理项目中的问题. 今天的任务总结如下: 团队成员 今日团队工作 陈少杰 调试后端连接的部分,寻找bug 王迪 测试搜索功能,修改b ...

  4. 读书笔记(chapter18)

    调试 18.1准备开始 18.2内核中的bug 1.从隐藏在源代码中的错误到展现在目击者面前的bug,往往是经历一系列连锁反应的事件才可能触发的 18.3通过打印来调试 1.健壮性 健壮性是print ...

  5. A总结

    Alpha 答辩总结 评审表 组名 格式 内容 ppt 演讲 答辩 总计 天机组 15 15 14 15 14 73 PMS 16 16 15 15 16 78 日不落战队 16 16 16 15 1 ...

  6. ElasticSearch 2 (7) - 基本概念

    ElasticSearch 2 (7) - 基本概念 摘要 ElasticSearch的一些基本核心概念,理解这些概念有助于ElasticSearch的学习 准实时NRT(Near Realtime) ...

  7. 组件 --BreadCrumb--面包屑

    面包屑组件多用于导航栏,对于大型网站,做面包屑导航栏 .breadcrumb .breadcrumb-item .active:表示现在正处在该页面 效果截图: 代码: <nav> < ...

  8. JMeter学习笔记——认识JMeter(1)

    拿到一个自动化测试工具,我们第一步就应该了解它能提供我们哪方面的功能(最直接的方法就是从官网获取),接下来就是简单的对这个工具进行“功能测试”了,当然这里的功能测试不是让你找它存在的bug,而是让自己 ...

  9. Linux命令(四)删除文件 rm

    用户可以使用 rm 命令删除不需要的文件. rm 可以删除文件或目录,并且支持通配符. 如果目录中存在其它文件则会递归删除. 删除软链接只是删除链接,对应的文件或目录不会被删除. 软链接类似于 win ...

  10. mybatis 注解和xml 优缺点

    xml: 增加了xml文件,修改麻烦,条件不确定(ifelse判断),容易出错,特殊转义字符比如大于小于 注释: 复杂sql不好用,搜集sql不方便,管理不方便,修改需重新编译 #和$区别: 相同 都 ...