实例 
以’div > span p span:last’这个选择符为例,看看它的调用链是如何顺次完成的。
根据对源码的剖析,理解如下:
jquery.init -> jquery.prototype.find 
进入Sizzle(对xml的判断) -> 设置parts数组等在匹配中所需要的元素 -> 根据数组长度以及调用origPos进行判断,来决定进入哪个分支,在这个实例下进入分支1 
循环调用Sizzle进行匹配,将结果存入set中(因为在这一过程中是循环调用,所以对Sizzle的判断也是需要多次,进入哪一分支当然也会是不一样的,比如第二轮循环判断则进入分支2中进行处理) ,对于>号的处理,也会将它合并在其后的span中,构成新的选择符 ‘>span’,然后进入Expr.relative进行匹配,同时调用posProcess。 
调用Sizzle.find 匹配除伪类以外的部分(即这里的选择器不包含:last),首先会调用Expr.find的find方法来判断是否为哪一类匹配,在这一实例中,为TAG匹配。 
对从4步中生成的对象进行过滤,匹配’>'(这一步的匹配是由Sizzle.filter触发,由Expr.relative完成),而在匹配’span:last’时则由posProcess来触发,设置later值(:first)以及selector(span),对span的匹配和4步骤一样,重复匹配,而对:first的匹配则是第5步的重头戏,也就是调用Sizzle.filter来完成, 由此便生成了最后的匹配结果。 
对于有‘,’这样需要合并的选择器,Sizzle在获取结果后会按照文档流进行排序。所以,你可能会遇到这样的问题:把一个结果集append到新的节点后,新的节点可能不会按照你书写的选择器的顺序出现。
以上,可以得出以下结论:大致通过如下步骤来完成:1.对表达式分组。
2.选择合适的处理顺序。
3.在当前的上下文里过滤要找的节点。并更新上下文。重复这一过程,直到结尾。
4.对结果排序,如果需要的话。
5.返回结果数组。前向兼容 
querySelectorAll 
其实不止这一处,在Sizzle的API手册中Internal部分的find 函数(与filter构成了Sizzle的两把宝剑),在传递进该方法的参数可以用 querySelectorAll() (依赖于当前的浏览器执行环境) 直接获取时,它则直接调用该方法,既拥有了向前兼容的特性,又达到了速度的提升。虽然有些环境实现了方法querySelectorAll,但是会有bug。//如果当前document 支持 querySelectorAll方法,则将浏览器可以完成的匹配完全交给浏览器 
if ( document.querySelectorAll ) (function(){    
     var oldSizzle = Sizzle;    
     // 解决Safari bug 略过 ...    
     Sizzle = function(query, context, extra, seed){        
       context = context || document;     
     // 因为querySelectorAll 在domElement 节点上操作时,存在bug 所以多了这样的判断    
     // bug info: http://ejohn.org/blog/thoughts-on-queryselectorall/        
        if ( !seed && context.nodeType === 9 && !isXML(context) ) {           
             return makeArray( context.querySelectorAll(query), extra );        
      }        
     // querySelectorAll 可用则直接返回结果,否则才调用 sizzle        
     return oldSizzle(query, context, extra, seed);   
     };    
     // oldSizzle 方法追加进 新的 Sizzle 中  
})();对于任何一个开发者,我想,若浏览器原生已提供了实现方法,他都不会去高效而求繁琐吧。这一点在Sizzle中得到了充分的体现,总是尽可能的使用相应环境下已实现的原生方法。所以在IE的低版本中(比如IE6)Sizzle的表现更加出众,在高级的浏览器中的对比却没有那么大的差别。扩展 
如何定义自己的选择器呢.如果项目中频繁使用某些过滤规则,是不是把它作为一个选择器更有效呢。
既然javascript的对象可以任意扩充,只要我们访问得到,那么我们就可以很轻松得创建出自己的选择器
在jQuery.expr.filter对象中,有很多内置的选择器,比如 ‘disabled’,'text’,那我们就扩充它,比如,想寻找包含span的div元素// filter的简写  ':'
jQuery.expr[":"] = jQuery.expr.filters;
$.extend($.expr[':'], {    
     hasSpan: function(e) {       
        return $(e).find('span').length > 0;    
      } 
});这样,我们就拥有了 ‘:hasSpan’ 的选择器,使用当然和默认的一样。//直接用就可以了
$('div:hasSpan')....比较 
再拾YUI3,在经过大幅度变化,以全新姿态出现,从选择器上的执行上效率不逊色于Sizzle几毫,初看YUI时就一直对它的模块细粒度化赞不绝口,但是从如我这样的实用主义者的角度来看,选择器就应该是一个单独的模块,就如同jQuery分离而出的Sizzle。但在YUI term眼里,为了让代码的组织结构看上去更加的理想化,更加具有”YUI3“的特色,将之在代码结构上又细分出一二三,比起Sizzle的简洁,它显得太过学院派。
除此,在选择器的扩展上,sizzle表现胜于YUI3 selector,在实现css1~css3选择器的基础上,又对常用的功能进行了扩展。比如对表单元素快捷操作。据我所知,开发者对这类型选择器的使用频率并不是想象中那么低。既然有了模块的细分,为什么不将这部分作为一个可扩充性的功能点模块融入框架中呢。Sizzle于开发者就如同一块可口味佳的点心,满足我们各式各样的胃口,简洁,不失其功能的强劲,这点非常值得称道。
总得来看,Sizzle与YUI就好象一个面向实际与理想主义的比较。这里没有对错之分,从不同角度来看,都能略窥其各自的禅意。前者从如何为开发者带来便利的角度考虑,让开发者实时觉得它的简单可信赖。后者,也寄托了自己对web的构想,如果浏览器原生全部支持css3-selector,那岂不是完全不用引入该模块了,不过我想,真到那时候,各框架也都会有很大的变化了,只是我对这一天的到来抱有比较消极的态度,这是后话。
总结 
本文从总体上讨论了jQuery之Sizzle选择器的实现原理,通过一个初步的流程分析,让各位读者对此有一个大致印象,毫无疑问,更深层次的匹配,也只是它的递归调用,再匹配而已。
这里,没有做与其他框架在效率上的比较,如果你还对它的效率还有所怀疑的话,你可以自行比较。
如果你感兴趣,更推荐你继续去探索在1.4中着重优化的api源码,或许,会给你更多的启示
思考 
从jQuery的角度来讲,Sizzle的出现随之也带来了web上的一些新的局面,在追求效率的同时,即使是这类单种子模式的库也是需要将之分离开来,来设计成能够独立使用,独立维护的引擎。
从选择器的角度来讲,Sizzle这次算法的提升,我初步的结论是它结合了DOM这一特定的数据结构,使其每次的匹配能够更精准,以此获得引擎效率上的提升。
我们可否多想想,在思维的开拓上能给我们留下多少财富。很多问题的解决,在换一种新的思维方式后,是不是常常会有柳暗花明的感觉呢。
相关参考: 
W3C css selector:CSS1,CSS2,CSS3
CSS Support History -Brian Wilson
CSS Selectors and Pseudo Selectors and browser support – kimblim.dk
Javascript CSS Selector Engine Timeline – Paul Irish

解决方案 »

  1.   

    这样的论文式文章的写法,对于讲求实用的技术者来说,是很不容易感兴趣的。
    如果作者要出这样的书,那恐怕卖不了几本。
      

  2.   

    到了文末,终于在总结和思考部分,隐约透露出对这一技术的定性评价。可是,有多少读者还会对这篇文章在看完前半部分之后保持兴趣而看到这里呢?
      

  3.   

    的确,在调动读者阅读兴趣方面的功力几乎谈不到。
      

  4.   

    不管怎樣,我們還是要感謝樓主的分享
    希望樓主再接再厲,寫出更精采的文章