什么是词法分析器(tokenize)?
词法分析器又称扫描器,词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号以供后续语法分析使用
'div > div.aaron input[name=ttt]'
是一个相对复杂的选择器,这样的结构在不支持高级API的浏览器中是无法直接通过获取的,那么所有选择器的库就必须要干的一件事,把复杂选择器按照一样的设计规则,分解成浏览器原始API能够识别的结构,然后通过其他的方法找个这个结构。所以这里就要引入一个切割的算法了,也有点类似编译原理的词法分析。
所以引擎在遇到无法直接处理的复杂选择器时,就需要按照内部的规则进行分组了。
Sizzle的tokenize格式如下 :
{ value:'匹配到的字符串', type:'对应的Token类型', matches:'正则匹配到的一个结构' }
tokenize需要解析的几种情况:
多重选择器,逗号分组
selector = 'div,input'
在出现逗号分隔符的时候,就说明选择所有指定的选择器的组合结果,所以需要分割成各自的处理模块,这种事情当然交给正则来做是最合适的
A: 常规的思路先是通过split(,)先把选择器劈成二部分,然后依次处理各自的模块
B:sizzle的思路则是循环一个一个分组出来的
我们假设一个复杂的选择器
div.aaron > input[name=ttt] , div p > span
这里涉及了3大块
1、分组逗号
2、层级关系
3、每种元素处理
具体我们参考下我写的代码区域
$("#test2").click(function() { //sizzle解析区域 }
其中分3大块的处理,其实很明了,因为这个案例的问题,我们没有层级关系的具体代码,下章会详细讲解,我们先理解这个思路。
sizzle对于分组过滤处理都用正则,其中都有一个特点,就是都是元字符^开头,限制匹配的初始,所以tokenize也是从左边开始一层一层的剥离。
其中会用到的正则:
//分组 var rcomma = /^[\x20\t\r\n\f]*,[\x20\t\r\n\f]*/; //关系符 var rcombinators = /^[\x20\t\r\n\f]*([>+~]|[\x20\t\r\n\f])[\x20\t\r\n\f]*/; //空白 var whitespace = "[\\x20\\t\\r\\n\\f]";
所以最终的结构:
groups: [ tokens: { matches: ? type : ? value : ? }, tokens: { matches: ? type : ? value : ? } ]
当然为什么是这样的结构,这是sizzle内部的一个解析规则罢了。后面就用这个规则去做匹配与筛选。
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://code.jquery.com/jquery-latest.js"></script> <title></title> </head> <body> 多重选择器,逗号分隔处理 <button id="test1">通过split分组</button> <button id="test2">sizzle的分组方式</button> <div id="text"> <p> <input type="text" /> </p> <div class="aaron"> <input type="checkbox" name="readme" /> <input type="checkbox" name="ttt" /> <input type="checkbox" name="aaa" /> <p>慕课网</p> </div> </div> <div> <input type="checkbox" name="readme" /> <input type="checkbox" name="ttt" /> <input type="checkbox" name="aaa" /> <p>Aaron</p> </div> <script type="text/javascript"> //最简单的选择器 var selector = 'div,input'; //常规分组,可以通过split $("#test1").click(function() { var soFar = selector.split(","); alert(soFar[0]) alert(soFar[1]) }) //sizzle的分组方式 $("#test2").click(function() { //分组 var rcomma = /^[\x20\t\r\n\f]*,[\x20\t\r\n\f]*/; //TAG var TAG = /^((?:\\.|[\w*-]|[^\x00-\xa0])+)/; var soFar = selector; var match; var matched; var tokens; var groups = []; while (soFar) { //第一大块,分组关系 //查找最左边的选择是不是逗号开头 //matched用于处理第一进入 //因为div input,div p 开始分解,第一个不是特殊符号 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 = [])); } //退出处理 matched = false; //第二大块,层级关系 // // 这里用的最简单的处理关系, // 不涉及层级关系 // ............................ // //第三大块,选择器 match = TAG.exec(soFar) matched = match.shift(); tokens.push({ value : matched, type : 'TAG', matches : match }); soFar = soFar.slice(matched.length); if (!matched) { break; } } alert(groups) }) </script> </body> </html>