這是我第一篇文章,最近看司徒大大的《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所有的正則