Javascript 选择器(selector engine)似乎从 jQuery 流行以来就大行其道,改变了原有 Javascript 选择 DOM 节点的方式。
目前 Javascript 选择器也有众多的选择,包括能列举出来的就有 Peppy 、 Sizzle 以及 Sly 等,它们都实现了所有的 CSS3 选择器 并且性能不俗。
而此次特别要推荐 Mini 的因素有很多。其最大的亮点就是从实用主义出发,简单高效的完成任务。
实用主义
jQuery 的作者 John Resig 曾经统计 jQuery 框架常用的几个选择器 。 很惊讶的发现用户其实常用 tagName、className 以及 id 就能完成 95% 以上的工作。
而 Mini 就从实用出发,它并没有标榜自己实现了全部 CSS3 的所有选择器,只是实现了下面的选择器及其变种:
- div
- .example
- body div
- div, p
- div, p, .example
- div p
- div > p
- div.example
- ul .example
title
- h1#title
- div #title
- ul.foo > * span
是的,虽然不多,但是相信在日常中已经足够使用。
实用主义虽然让 Mini 提供的功能有限,但从个侧面带来的优势就是 性能方面非常的理想 。 当然这还有与它的内部实现有关。
简单至上
Mini 的代码很简单,甚至不用恐惧于如何去读懂 Sizzle 这样的每行源代码。先从它的调用函数 _find 看起,往下阅读几行,你就发现它性能的秘密
if (!simple && context.querySelectorAll) {
return realArray(context.querySelectorAll(selector));
}
很多的现代浏览器(包括 Gecko、WebKit 等)都 提供了原生的 Javascript 选择器支持 。 Mini 充分得利用了这点,从而将大部分的工作交给了浏览器(也免除了日后的维护之忧)。这点非常值得称道, 也是我们以后写 Javascript 应该走的方向 -- 要基于浏览器提供的功能而非类型进行对应的开发。
_find 其后的逻辑非常的简单,使用几个正则将提供的选择器解析出来,分别根据 getElementsByClassName、 getElementsByClassName 以及 getElementById 获取节点。
继续根据上面的思路,由于陈旧的 IE 没有既没有提供 querySelectorAll 也没有提供 getElementsByClassName, 那么它只能走到 else 的尽头,使用 filterByAttr 函数。
取到对应 className 以及 tagName 后,还得根据选择器获取其与其相符的节点,那么就轮到 filterParents 干活了。
filterParents 这个函数也是相对 _find 比较核心的函数,而且也是主要的性能消耗点。filterParents 其实要做的和 _find 类似,唯一不同的是它根据关系符向上剔除未满足条件的节点。
通过上面两个函数所得到的节点,通常已经是满足选择符条件的节点了。 但是可能的情况就是有重复的节点,这时就该 unique 函数上场,顾名思义剔除重复的节点。
这里要提到的就是 unique 函数做了个简单的 memoization 实现 , 作者的用心可谓从细节方面体现得淋漓尽致。还有个小技巧就是
var uid = +new Date();
竟然可以用这样直接返回时间戳,俏皮的代码让人再次感到犹如嚼了口薄荷糖,非常的清新(好吧,我火星了)。
读码到最后,有一点思考就是为何每次都要使用 realArray 函数强制将 NodeList 转换成 Array?虽然使用 querySelectorAll 返回的是 NodeList ,但是也是可以使用 Array 操作迭代。作者这样的做法我推测,可能是出于返回数据类型一致的缘故。
观点
说说到我的个人观点,这可能会让这篇文章前后矛盾。Javascript 选择器(selector engine)我个人不是非常推荐使用。
原因主要有两点,其一就是性能,其二就是会让写出来的代码过于的依赖于 DOM 结构。但能读到如 Mini 这样的代码,窥其内部运作机制,未尝不是件非常让人愉悦的事情。
最后,说说 Mini 的作者 James Padolsey 。我本人也很难相信,他竟然只有 19 岁!更难 得的是在 他 Blog 上的 About me 页面 中写道
What services do I currently offer?
- Everything JavaScript!
同时, 他也是 jQuery Cookbook 的作者 。 「小小年纪」能够有这样的心态以及成就,让我们这帮「前端老古董」感到唏嘘不已 :^)
附
Bang 兄 已对 Mini 的代码做了更为详尽的解析,有兴趣的可以参看 他的相关 Blog 。