Fork me on GitHub

sizzle分析记录:词法分析器(tokenize)

词法分析器(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 );
    },

 

伪类放下一章

posted on 2014-07-14 18:01  【艾伦】  阅读(2946)  评论(1编辑  收藏  举报