詞法分析器(tokenize)?
詞法分析器又稱掃描器。詞法分析是指將我們編寫的文本代碼流解析為一個一個的記號,分析得到的記號以供后續語法分析使用。
sizzle引入了tokenize這個概念,意義?
jQuery的選擇器,實現了css1-css3的API,但是ECMAScript低版本的API中本身沒有針對這種CSS的處理API,在IE8以上就引入了querySelectorAll
各種瀏覽器實現還有差異,這里不是主題,我們看如果是低版本的接口要如果處理復雜的CSS選擇器
首先面臨的就是要對復雜的選擇器進行分解
例如:
div > div.Aaron p span.red
只能針對每個版本的瀏覽器的支持力度去匹配各自的選擇
所以此時會引入一個詞法分析器(tokenize)用來把用戶復雜的匹配選擇,分解成各自的單元,可以提供給后面對應的接口處理
選擇器總的來說分四大類:
並聯就是合並分組,用逗號分隔
簡單的選擇器,ID,TAG,CLASS,ATTR,*
關系選擇器:> ,+ , ~, 空格
偽類:動作偽類,目標偽類,語言偽類,狀態偽類,結構偽類,取反偽類
Sizzle的Token格式如下 :
{ value:'匹配到的字符串', type:'對應的Token類型', matches:'正則匹配到的一個結構' }
tokenize需要解析的幾種情況:
情況一:多重選擇器分組
soFar :$("div, span, p.myClass" )
在出現逗號分隔符的時候,就說明選擇所有指定的選擇器的組合結果,所以需要分割成各自的處理模塊
這種事情當然交給正則來干是最合適的
常規的思路先是通過split(,)先把選擇器劈成三部分,然后依次處理各自的模塊
jQuery對於過濾正則都有一個特點,就是都是元字符^開頭,開限制匹配的初始,所以tokenize也是從左邊開始一層一層的剝離
rcomma.exec( soFar )) var whitespace = "[\\x20\\t\\r\\n\\f]"; var rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" )
匹配第一個出現的非顯示字符
換句話匹配
, span, p.myClass
然后在划分容器
if ( !matched || (match = rcomma.exec( soFar )) ) { if ( match ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } groups.push( (tokens = []) ); }
其結果就是:(結構不合理,先理解這個意思)
groups:[
tokens :{div, span},
tokens :{p.myClass}
]
情況二:關系處理器分組
在層級關系中有幾種特殊的划分 Token : >, +, 空格, ~ 用來表明父與子,兄弟,祖輩子孫之間的層級關系
$( "ul.topnav > li" )
從 > 划分
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
可以是>+~或者空白
這個分組是為了之后的關系選擇確定
if ( (match = rcombinators.exec( soFar )) ) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace( rtrim, " " ) }); soFar = soFar.slice( matched.length ); }
剩余幾種Token :
Expr.filter :TAG, ID, CLASS, ATTR, CHILD, PSEUDO
通過一系列的正則抽出表達式中的內容
ID:
///^#((?:\\.|[\w-] | [^\x00-\xa0] ) +)/ var characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+"; var ID = new RegExp("^#(" + characterEncoding + ")") console.log(ID.exec("#div > li")) //["#div", "div", index: 0, input: "#div > li"]
TAG:
var TAG = new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ); console.log(TAG.exec("li > sadf.da dsf")) //["#div", "div", index: 0, input: "#div > li"]
CLASS:
var Class = new RegExp( "^\\.(" + characterEncoding + ")" ); console.log(Class.exec(".li > sadf.da dsf"))
ATTR:
屬性選擇器有點復雜,通過第一次正則只能匹配器出整體,所以需要第二次分解,引入了Expr.preFilter
Expr.preFilter保留了3個兼容處理分別是ATTR,CHILD,PSEUDO復雜的選擇器
var identifier = characterEncoding.replace( "w", "w#" ); var attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]"; var ATTR = new RegExp( "^" + attributes ); console.log(ATTR.exec("[name*='man']")
preFilter:
preFilter: { "ATTR": function( match ) { match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; } return match.slice( 0, 4 ); },
偽類放下一章