在Sizzle里,大体思路,当为复合选择器时,判断是否支持querySeletorAll,如果不支持则调用自写方法select。
select的功能十分冗长,下面先分析tokenize
在tokenize函数的作用是将形如'ul.topnav > li,div'的选择器解析为
[ [{value:'ul',type:"TAG",matches:['ul']},{value:'.topnav',type:"CLASS",matches:['topnav']},{value:' > ',type:">"},{value:'li',type:"TAG",matches:['li']}], [{value:'div',type:"TAG",matches:['div']}] ]
每个逗号解析为一个数组,表示一个独立选择器项,每个独立选择器项的选择关系按照CLASS,TAG,ID和关系解析成如上格式。
这种解析的类型是下面进行查找元素的固定格式,先不考虑后面,我们先分析tokenize的实现。
//缓存函数创建函数 var createCache = function(){ //缓存函数共用数组 var keys = []; return function cache(name,value){ // 当长度大于某常数时,清除老元素,插入新元素 if( keys.push( name ) > 1){ delete cache[ keys.shift() ] }; return cache[name] = value; }; } //缓存函数对象 //这是一个方法,通过这个方法可以将缓存数据保存在这个函数(也是对象)里。 var tokenCache = createCache(); //简易正则过滤对象 var filter = { TAG: /^(\w+)/, ID: /^#(\w+)/, CLASS: /^\.(\w+)/ } //关系匹配正则 var relation = /^\s*([\>\+\~]|^\s)\s*/; //逗号正则 var comma = /^\,/; function tokenize(selector){ var cached = tokenCache[selector]; //缓存中有结果,直接返回 if(cached){ return cached; } // 循环条件 var sofar = selector, // 结果数组 groups = [], // 匹配参数 matched, // 一个独立选择器项 token = [], //辅助参数正则匹配结果 match; //循环解析选择器 while(sofar){ //首次默认创建一个单独选择器项 //之后通过判断逗号创建 if(!matched || (match = comma.exec(sofar)) ){ if(match){ //保存未解析的选择器 sofar = sofar.slice(match[0].length); } groups.push(token = []); } //每次循环设置辅助匹配参数为false,如果之后仍无匹配不存在通过break跳出循环 matched = false; //匹配关系>等 //解析保存结果 if(match = relation.exec(sofar)){ matched = match.shift(); token.push({ value: matched, type: match[0].replace( /\s+/g, " " ) }) sofar = sofar.slice(matched.length) } //匹配选择器CLASS,TAG,ID //解析保存结果 for(var type in filter){ if(match = filter[type].exec(sofar)){ matched = match.shift(); token.push({ value: matched, type: type, matches: match }) sofar = sofar.slice(matched.length) } } //没有跳出循环。 if ( !matched ) { break; } } //缓存结果并返回 return (tokenCache(selector,groups)); } console.log(tokenize('ul.topnav > li,div')) console.log(tokenize('a li'))
这是我模拟输入输出结果写出的函数,jQuery源码中在实现主要功能的基础上进行了大量兼容处理。
缓存在jQuery里面非常常用,在这里重新说一遍基本思路是,将函数输入值key,和输出值保存于缓存对象中,等到下次调用函数,如果缓存对象中有值,则直接读取。
本例的缓存略微复杂,它是利用函数将缓存数据保存于同名对象中。又,作者希望几种缓存共用长度,在这里用了创建缓存函数的函数。