1.前言Sizzle最初是jQuery中的一个选择器引擎,后来逐渐独立出来,成为一个独立的模块,可以自由引入到其他库中。我以前在YUI3中把它当作一个模块来使用,使用起来很顺畅,没有任何障碍。Sizzle发展到现在,以jQuery1.8为分水岭,大致可以分为两个阶段。后来的版本引入了编译功能的概念,Sizzle的源代码变得更难阅读,不再兼容低版本的浏览器,而且看起来更零散。这次在Sizzle第一期看了jQuery1.7的最终版,收获颇丰。一方面是框架设计的思想,另一方面是编程技巧。2、jQueryconstructorSizzle来源于jQuery,而jQuery是一个基于DOM操作的类库,所以在学习Sizzle之前有必要了解一下jQuery的整体结构:(function(window,undefined){varjQuery=function(selector,context){returnnewjQuery.fn.init(selector,context,rootjQuery);}jQuery.fn=jQuery.prototype={...}jQuery.fn.init.prototype=jQuery.fn;//工具方法//Deferred//支持//数据缓存//队列//属性//事件//大约2k行//DOM//css操作//Ajax//动画//仓位账户window.jQuery=window.$=jQuery;})jQuery具有很强的工程性。一个接口可以处理多个输入,这是jQuery易于使用的主要原因。相应的,这么大功能的API内部实现也相当复杂。要搞清楚jQuery和Sizzle的关系,首先得从jQuery的构造函数说起。经过梳理,明确了构造函数的处理逻辑。在下表中,jQuery的构造函数要处理6种情况,但Sizzle选择器引擎仅在处理选择器表达式时被调用。3.Sizzle的设计思路对于一个复杂的选择器表达式(下面讨论的前提是浏览器不支持querySelectorAll),如何处理?3.1拆分分析对于复杂的选择器表达式,nativeAPI不能直接解析,但是可以对其中的一部分进行操作,所以很自然地采取先局部后全局的策略:将复杂的选择器表达式拆分成一个块表达式和块之间的关系。如下图可以看出,1.选择器表达式按照block之间的关系进行拆分;2、块表达式中有很多伪类表达式,这是Sizzle的一大亮点,它也可以3、拆分块表达式可能是简单选择器、属性选择器和伪类表达式的组合,比如div.a,.a[name="北京"]。3.2块表达式查找在块内查找得到元素的基本集合,那么如何处理块与块之间的关系呢?通过观察可以发现,一个复杂的选择器表达式有两种顺序:从左到右:内部遍历得到的集合一个一个的得到一个新的元素集合,只要还有剩余的代码块,就需要continue反复重复搜索和过滤操作。总结就是:多重搜索和过滤。从右到左:得到的元素集必须包含最后一个元素,存在冗余不合格元素,接下来就是不断过滤剔除不合格元素。对于“相邻兄弟关系(+)”和“后兄弟关系(~)”,无论使用哪种方法,效率上都没有区别。但“父子关系”和“祖孙关系”则不同。这时候滋滋主要是从右往左选择。下面从两个维度进行说明:a.从左到右的设计思路:不断查询,不断缩小上下文,得到新的元素集从右到左:一次查询,多次过滤,第一次搜索得到的元素集不断缩小,直到最后的集合b,DOM树向左到right:从DOM上层向右底层需要不断遍历子元素或后代元素,且一个元素节点的子元素或后代元素个数未知或较大。父元素或祖先元素,一个元素的父元素或祖先元素的数量是固定的或有限的,但是从右到左是违反我们习惯的,这样做会不会有问题?答案是会有错误。PleaselookatasimpleDOMtreebelow:Intheaboveexample,weseethatwhenthereisapositionalpseudo-classintheselectorexpression,anerrorwilloccur.在这种情况下,没有办法准确。是第一个,只能从左到右选择。结论:对于性能触发器,使用从右到左;为了准确性,对于位置伪类,只使用从左到右。4.Sizzle具体实现4.1Sizzle整体结构}}else{sizzleengine实现,主要是模拟querySelectorAll}通过上面的代码可以看出,Sizzleselectorengine的主要工作是向上兼容querySelectorAllAPI。如果所有的浏览器都支持这个API,那么Sizzle就没有存在的必要了。关键函数介绍:Sizzle=function(selector,context,result,seed):Sizzle引擎的入口函数Sizzle.find:主要搜索函数Sizzle.filter:主要过滤函数Sizzle.selectors.relative:集合块间??关系处理函数{"+":function(){},"":function(){},">":function(){},"~":function(){}}4.2Splitanalysischunker=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^>+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g就是这样一个正则表达式,可以将复杂多样的选择器表达式分成若干块expressions有了block之间的关系,你是不是觉得blockexpressions是一项神奇的技术,可以抽象出复杂的问题,正则化的缺点是不利于阅读和维护,下图对其进行图形化分析:
