这是我第一篇文章,最近看司徒大大的《javascript框架设计》,看到了
Sizzle
这里,就想看看源码,上网找了文章版本都是1.7
以前的,并且很零零散散。因此打算本身写一些文章,从头至尾把Sizzle
读一遍。做为一个前端小白,确定是有不少看不懂的,但愿各位大佬能吹风磁暴帮帮我。我也在GitHub以注释的方式分析源码: 施工中。OK, 不废话了开始干。javascript
正则分两种写法,一种是字面量;一种是用RegExp
构造函数。当时用构造函数模式的时,所传入的字符串须要解析, 如:html
//这是字面量
var reg1 = /\\/;
//这是构造函数
var reg2 = new RegExp('\\\\');
复制代码
上面所示的两个正则是匹配的字符是相同的都是匹配一个\
, 可是传入构造函数的字符串要4个\
,缘由是在传入构造函数时,须要解析一次。这里能够去看红宝书的正则那一章写的很清楚。使用这种解析模式的正则读起来会特别的麻烦。Sizzle
用的就是这种解析。前端
Sizze中的正则主要是为了匹配选择器,区分是什么类型的选择器,进行分类。它拆分了几个基础的正则字符串,并再最后进行组合拼接,而后经过构造函数,new
出实例。
java
因为Sizzle的正则都是构造函数模式的,我会在它的代码下面注释一个字面量模式的方便阅读。git
booleans
先从最简单的开始,booleans就是一堆或,这里应该是进一步匹配没有值的伪类。好比::checked :disabled
,而不去匹配带值的,好比::nth-child(3)
。github
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" +
"ismap|loop|multiple|open|readonly|required|scoped",
复制代码
whitespace(空白)
这是Sizzle本身写的空白,与\s
的区别是少了一个\v
。正则表达式
这里为何少一个
\v
,我不太清楚,若是有知道的大佬,请告知谢谢。框架
whitespace = "[\\x20\\t\\r\\n\\f]"
//字面量
copy_whilespace = '/[\x20\t\r\n\f]/'
复制代码
identifier(标识符)
这个应该算是sizzle
正则表达式的核心了,它能够匹配.class
中的class
,能够匹配:nth-child(3)
中的nth-child
。
async
identifier = "(?:\\\\[\da-fA-F]{1,6})" + whitespace +
"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+"
//字面量
copy_identifier = '/(?:\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\[^\r\n\f]|[\w-]|[^\x00-\x7f])+/'
复制代码
首先identifier是一个非获取捕获,identifier匹配四种状况。ide
\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?
匹配的是 ASCII表。如\61
, 之因此是{1,6}
,应该支持二进制。\\[^\r\n\f]
匹配的是\
加不是\r\n\f
的任意可见或不可见字符, 如\a
。[\w-]
匹配的是任意可见字符或-
。[^\x00-\x7f]
匹配不是ASCII表中的全部字符这意味着,像诸如#.()~=!$
等等这些是不会被匹配到的。
attributes(属性)
attributes匹配的是属性选择器,能够匹配[attr]
, 能够匹配[attr $= 'val']
,可是不能匹配[attr =]
,也不能匹配[attr.]
attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
"*([*^$|!~]?=)" + whitespace +
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
whitespace + "*\\]";
//字面量 因为把identifier和whitespace都替换成正则的话 实在是太长了 这里直接用变量名代替了
//为了好阅读, 换点行
copy_attributes = `\[whitespace*(identifier) (?:whitespace*([*^$|!~]?=)whitesapce* (?: '((?:\\.|[^\\'])*)'| "((?:\\.|[^\\\"])*)"| (identifier) ) |)whitespace*\]`;
//真正的样子
true_attributes = /\[[\x20\t\r\n\f]*((?:\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\[^\r\n\f]|[\w-]|[^\x00-\x7f])+)(?:[\x20\t\r\n\f]*([*^$|!~]?=)[\x20\t\r\n\f]*(?:'((?:\\.|[^\\'])*)'|"((?:\\.|[^\\"])*)"|((?:\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\[^\r\n\f]|[\w-]|[^\x00-\x7f])+))|)[\x20\t\r\n\f]*\]/
复制代码
属性正则匹配两种状态的属性选择器:一种有等号的[attr=val]
,一种没有等号的[attr]
。将这两个区分开的是最外层的非捕获组的“或|
“。
属性正则比较复杂,可是若是按捕获组来看的话,就很清晰了。属性正则一共有五个捕获组,除了第一个属性名必定会有值以外,其他的捕获组须要知足必定的条件:
(identifier)
: 属性名。([*^$|!~]?=)
: 运算符。若是是[attr]
这种状况的话,值为空'((?:\\.|[^\\'])*)'
:用单引号括起来的属性值。[attr='val']
的val
"((?:\\.|[^\\\"])*)"
:用双引号括起来的属性值。[attr="val"]
的val
(identifier)
:没有用引号括起来的值。[attr=val]
的val
引号中匹配的值和没有引号匹配的值也是有区别的。如[class=".aa"]
能够匹配,可是[class=.aa]
就不能够。
pseudos(伪类)
pseudos匹配的是伪类选择器
pseudos = ":(" + identifier + ")(?:\\((" +
"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
".*" +
")\\)|)";
//字面量
copy_pseudos = `:(identifier) (?:\(( ('((?:\\.|[^\\'])*)'|"((?:\\.|[^\\"])*)")| ((?:\\.|[^\\()[\]]|attributes)*)| .* )\)|)`
//所有
true_pseudos = /:((?:\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\[^\r\n\f]|[\w-]|[^\x00-\x7f])+)(?:\((('((?:\\.|[^\\'])*)'|"((?:\\.|[^\\"])*)")|((?:\\.|[^\\()[\]]|\[[\x20\t\r\n\f]*((?:\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\[^\r\n\f]|[\w-]|[^\x00-\x7f])+)(?:[\x20\t\r\n\f]*([*^$|!~]?=)[\x20\t\r\n\f]*(?:'((?:\\.|[^\\'])*)'|"((?:\\.|[^\\"])*)"|((?:\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\[^\r\n\f]|[\w-]|[^\x00-\x7f])+))|)[\x20\t\r\n\f]*\])*)|.*)\)|)/
复制代码
伪类也有两种状况,一种:after
,另外一种是:nth-child(3)
。伪类有11个捕获组,可是$7-$11
都是attributes
的。
这里只看前6个捕获组:
$1
(identifier)
:伪类名。如 :nth-child(3)
中的nth-child
, :after
中的after
。$2
\((....)\)
:最外层的捕获组, 捕获括号内全部的内容。如:nth-child(3)
中的3
; :nth-child("3")
中的"3"
;:nth-child('3')
中的'3'
;:not(".className")
中的".className"
。$3
('((?:\\.|[^\\'])*)'|"((?:\\.|[^\\"])*)")
:小括号内使用引号的字符串。如:nth-child("3")
中的"3"
;:nth-child('3')
中的'3'
。$4
((?:\\.|[^\\'])*)
:单引号中的值。如:nth-child('3')
中的3
。$5
((?:\\.|[^\\"])*)
:双引号中的值。如:nth-child("3")
中的3
。$6
((?:\\.|[^\\()[\]]|attributes)*)
:除了()[]\
的字符串或者属性。如:nth-child(3)
中的3
;:not([href = 'aaa'])
中的[href = 'aaa']
。var rwhitespace = new RegExp( whitespace + "+", "g" ), //空白
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), //先后留白
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), //逗号
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), //组合器
rdescend = new RegExp( whitespace + "|>" ), //后代
rpseudo = new RegExp( pseudos ), //伪类
ridentifier = new RegExp( "^" + identifier + "$" ), //标识码
matchExpr = {
"ID": new RegExp( "^#(" + identifier + ")" ),
"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
"TAG": new RegExp( "^(" + identifier + "|[*])" ),
"ATTR": new RegExp( "^" + attributes ),
"PSEUDO": new RegExp( "^" + pseudos ),
/** /^:(only|first|last|nth|nth-last)-(child|of-type) (?:\(whitespace*( even|odd|(([+-]|)(\d*)n|)whitespace* (?:([+-]|)whitespace*(\d+)|) )whitespace*\)|)/i // $1 only|first|last|nth|nth-last // $2 child|of-type // $3 括号中所有的内容 // $4 even odd 或者 表达式2n+1 中的2n // $5 2n的正负 +2n -2n 中的 + - // $6 n的倍数 2n 中的 2 // $7 运算符 + - // $8 最后一个数 1 */
"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" ),
"needsContext": new RegExp( "^" + whitespace +
"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
},
rhtml = /HTML$/i,
rinputs = /^(?:input|select|textarea|button)$/i,
rheader = /^h\d$/i, //h1 h2 h3 h4 h5 h6..
rnative = /^[^{]+\{\s*\[native \w/,
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, //快速寻找ID CLASS 标签
rsibling = /[+~]/; //兄弟
复制代码
以上就是Sizzle所有的正则