Sizzle后记
- Time: 26 November 2012
- Categories:
- Frontend 27
- Tags:
- Sizzle 7
- jQuery 7
- Javascript 21
- chrome 2
- bug 1
###Sizzle的调试 为方便调试,我在Sizzle源码中插入了若干调试代码,使用Chrome浏览器打开测试页面,在控制台可以很直观地看到Sizzle的整个处理流程,如图:
Demo文件: sizzle.html
我们通过一些代码测试Sizzle从右向左过滤节点的优化带来的性能提升,下图中li a:first
中含有位置过滤器,采用了传统的从左往右查询,耗时约288ms,而li
a:first-child
是子级选择器,采用的是从右向左过滤的方式来查询,耗时仅约17ms。如图:
Sizzle的最佳实践
经过上面的文章分析,我们可以稍微总结出一些Sizzle(JQuery选择器)的最佳实践来提升页面性能。
- 尽量用id去获取DOM节点
- 使用tag.class代替.class
- 使用name=value来获取表单元素
- 尽量少用位置选择器,或尝试用伪类选择器代替
- 使用parent > child,而不是parent child
- DOM结构比较复杂的情况下,尽量缩小查找的上下文范围
- 尽可能缓层查询结果
- 书写良好的DOM结构
###阅读Sizzle时遇到的坑
####1. 正则的误解
/^\w+/.test()
这段代码运行后,返回是啥?反正我是中枪了,以为果断的返回false。
源码是这样的:
// 小生在一开始阅读时果断以为当match[3]为空,就false了,导致后面逻辑都理解错了。
if ((chunker.exec(match[3]) || "").length > 1 || /^\w/.test(match[3])) {
在控制台运行后,发现结果为true!猜想正则会对传入参数做类型转化,遂翻V8源码,得:
function RegExpExec(string) {
if (!IS_REGEXP(this)) {
throw MakeTypeError('incompatible_method_receiver', ['RegExp.prototype.exec', this]);
}
string = TO_STRING_INLINE(string);
var lastIndex = this.lastIndex;
//...
}
####2. Chrome下console的bug 该bug大致描述:Console bug about “lazy evaluating for Reference types variables” in webkit platform
var obj = new {};
obj.a = 1;
obj.b = 2;
console.log(obj);
delete obj.b;
console.log(obj);
期望结果:
{a:1, b:2}
{a:1}
实际结果:
{a:1}
{a:1}
可见webkit平台下的console对引用类型变量是滞后计算。当时我在看Sizzle源码的同时使用console跟踪每个流程中DOM节点变化,遇到这个坑,可以想想是多么的蛋疼(哥压根儿就没怀疑过console啊)。 我在sizzle.js中加入了自己的代码来Fix这个问题,原理非常简单:
// fix console bug ( lazy evaluation Reference types variables)
var clog = function () {
if (navigator.userAgent.indexOf('MSIE') + 1) { return ; }
var arg = arguments ,
rArrayLike = /object (?:Array|Object|NodeList)/g,
toString = Object.prototype.toString ;
Array.prototype.map.call(arg, function (item, i, o) {
if (toString.call(item).match(rArrayLike) != null) {
o[i] = Array.prototype.slice.call(item, 0);
}
})
console.log.apply(console, arg);
};