Sizzle 源碼分析討論(一): 正則表達式

前言

這是我第一篇文章,最近看司徒大大的《javascript框架設計》,看到了Sizzle這裏,就想看看源碼,上網找了文章版本都是1.7以前的,並且很零零散散。因此打算本身寫一些文章,從頭至尾把Sizzle讀一遍。做爲一個前端小白,確定是有不少看不懂的,但願各位大佬能吹風磁暴幫幫我。我也在GitHub以註釋的方式分析源碼: 施工中。OK, 不廢話了開始幹。javascript


正則表達式

正則分兩種寫法,一種是字面量;一種是用RegExp構造函數。當時用構造函數模式的時,所傳入的字符串須要解析, 如:html

//這是字面量
var reg1 = /\\/;
//這是構造函數
var reg2 = new RegExp('\\\\');
複製代碼

上面所示的兩個正則是匹配的字符是相同的都是匹配一個\, 可是傳入構造函數的字符串要4個\,緣由是在傳入構造函數時,須要解析一次。這裏能夠去看紅寶書的正則那一章寫的很清楚。使用這種解析模式的正則讀起來會特別的麻煩。Sizzle用的就是這種解析。前端


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]/'
複製代碼
  • \x20 表示ASCII 十六進制第20個字符 也就是空白
  • \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

  1. \\[\da-fA-F]{1,6}[\x20\t\r\n\f]?匹配的是 ASCII表。如\61, 之因此是{1,6},應該支持二進制。
  2. \\[^\r\n\f]匹配的是\加不是\r\n\f的任意可見或不可見字符, 如\a
  3. [\w-]匹配的是任意可見字符或-
  4. [^\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]。將這兩個區分開的是最外層的非捕獲組的「或|「。
屬性正則比較複雜,可是若是按捕獲組來看的話,就很清晰了。屬性正則一共有五個捕獲組,除了第一個屬性名必定會有值以外,其他的捕獲組須要知足必定的條件:

  • $1 (identifier): 屬性名
  • $2 ([*^$|!~]?=)運算符。若是是[attr]這種狀況的話,值爲空
  • $3 '((?:\\.|[^\\'])*)'用單引號括起來的屬性值[attr='val']val
  • $4 "((?:\\.|[^\\\"])*)"用雙引號括起來的屬性值[attr="val"]val
  • $5 (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所有的正則

相關文章
相關標籤/搜索