Math.pow(1.1, Day)

Sizzle后记

###Sizzle的调试 为方便调试,我在Sizzle源码中插入了若干调试代码,使用Chrome浏览器打开测试页面,在控制台可以很直观地看到Sizzle的整个处理流程,如图:

sizzle console process

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);
};