jQuery技术内幕:深入解析jQuery架构设计与实现原理. 3.3 设计思路

3.3 设计思路

在正式开始分析Sizzle的源码实现之前,先来讨论和分析下如果要执行一段选择器表达式,或者说设计一个简化版的选择器引擎,需要做些什么工作。下面以"div.red>p"为例来模拟执行过程,具体来说有从左向右查找和从右向左查找两种思路:

1)从左向右:先查找"div.red"匹配的元素集合,然后查找匹配"p"的子元素集合。

2)从右向左:先查找"p"匹配的元素集合,然后检查其中每个元素的父元素是否匹配"div.red"。

无论是从左向右还是从右向左,都必须经历下面3个步骤:

1)首先要能正确地解析出"div.red>p"中的"div.red"、"p"和">",即解析出选择器表达式中的块表达式和块间关系符。这一步是必需的,否则根本无从下手。

2)然后要能正确地找到与"div.red"或"p"匹配的元素集合,即查找单个块表达式的匹配元素集合。以"div.red"为例,可以有两种实现方式:

a.?先查找匹配"div"的元素集合,然后从中过滤出匹配".red"的元素集合。

b.?先查找匹配".red"的元素集合,然后从中过滤出匹配"div"的元素集合。

不管采用以上哪种方式,这个过程都可以分解为两个步骤:第一步用块表达式的一部分进行查找,第二步用块表达式的剩余部分对查找的结果进行过滤。

3)最后来处理"div.red"和"p"之间的关系符">",即处理块表达式之间的父子关系。在这一步骤中,从左向右和从右向左的处理方式是截然不同的:

a.?从左向右:找到"div.red"匹配的元素集合的子元素集合,然后从中过滤出匹配"p"的子元素集合。

b.?从右向左:检查每个匹配"p"的元素的父元素是否匹配"div.red",只保留匹配的元素。

无论采用以上哪种方式,这个过程都可以分解为两个步骤:第一步按照块间关系符查找元素,第二步用块表达式对查找的结果进行过滤。不论元素之间是哪种关系(父子关系、祖先后代关系、相邻的兄弟关系或不相邻的兄弟关系),都可以采用这种方式来查找和过滤。

另外,如果还有更多的块表达式,则重复执行第3步。

对于前面的3个步骤,可以进一步提炼总结,如下:

1)处理选择器表达式:解析选择器表达式中的块表达式和块间关系符。

2)处理块表达式:用块表达式的一部分查找,用剩余部分对查找结果进行过滤。

3)处理块间关系符:按照块间关系符查找,用块表达式对查找结果进行过滤。

从前面对选择器表达式的执行过程的分析,还可以推导分析出以下结论:

从左向右的总体思路是不断缩小上下文,即不断缩小查找范围。

从右向左的总体思路是先查找后过滤。

在从左向右的查找过程中,每次处理块间关系符时都需要处理未知数量的子元素或后代元素,而在从右向左的查找过程中,处理块间关系符时只需要处理单个父元素或有限数量的祖先元素。因此,在大多数情况下,采用从右向左的查找方式其效果要高于从左向右。

在了解了两种执行思路后,现在再来看看Sizzle,它是一款从右向左查找的选择器引擎,提供了与前面3个步骤相对应的核心接口:

正则chunker负责从选择器表达式中提取块表达式和块间关系符。

方法Sizzle.find( expr, context, isXML )负责查找块表达式匹配的元素集合,方法Sizzle.filter( expr, set, inplace, not )负责用块表达式过滤元素集合。

对象Sizzle.selector.relative中的块间关系过滤函数根据块间关系符过滤元素集合。

函数Sizzle( selector, context, results, seed )则按照前面3个步骤将这些核心接口组织起来。

本节对选择器引擎和Sizzle的设计思路作了探索和概述,接下来看看Sizzle的源码实现。

上一篇:ssh客户端显示中文问题的解决


下一篇:mysql5.7在centos上安装的完整教程以及相关的“坑”