JavaScript 压缩中的常量内联

2010-12-01 07:24

JavaScript 压缩中的常量内联

by lifesinger

at 2010-11-30 23:24:50

original http://lifesinger.org/blog/2010/11/const-inline-in-js-compress/

两年前,NCZ 在 Extreme JavaScript Compression With YUI Compressor 里提到:

Best Optimization = Identifier Replacement

标志符替换(Identifier Replacement)在 YUI Compressor 里非常重要。
下面用 CC 表示 Closure Compiler, YC 表示 YUI Compressor.

本地文件大小

为了帮助 YC 有效进行标志符替换,下面这段代码 toogle.js:

function toggle(element) {
    if(YAHOO.util.Dom.hasClass(element, 'selected')) {
        YAHOO.util.Dom.removeClass(element, 'selected');
        return false;
    } else {
        YAHOO.util.Dom.addClass(element, 'selected');
        return true;
    }
}

推荐写成 toogle2.js:

function toggle(element) {
    var Dom = YAHOO.util.Dom,
        SELECTED = 'selected',
        ret = false;

    if(Dom.hasClass(element, SELECTED)) {
        Dom.removeClass(element, SELECTED);
    } else {
        Dom.addClass(element, SELECTED);
        ret = true;
    }
    return ret;
}

源码和压缩后的文件大小对比:

很明显,用 YC 压缩的文件最小。这是因为用 CC 压缩时,会将常量 SELECTED = 'selected' 内联回去 toggle2-cc-min.js

function toggle(a){var b=YAHOO.util.Dom,c=false;
if(b.hasClass(a,"selected"))b.removeClass(a,"selected");
else{b.addClass(a,"selected");c=true}return c};

伟大的 gzip

从上面看起来,CC 将常量内联的做法很恼人。好在真实环境下,绝大部分服务器都开启了 gzip 压缩。来看一张图:

很囧,toggle2-yc-min.js 最大。
更囧的是,toggle2 的压缩版本,比原始 toggle 的压缩版本都大。
折腾了半天,居然帮了倒忙!

产生这种结果的原因是:gzip 算法里,对重复字符做了处理。重复字符越多,压缩比越大。toggle2.js 增加了代码,减少了 toggle.js 的重复字符。对于该特例来说,我们的确帮了倒忙。注意一般情况下,toggle2.js 的写法还是有益的。

CC 的常量内联

CC 的常量内联,有时会导致压缩后,文件大小暴增,比如将
http://lifesinger.github.com/lab/2010/cc-inline/test.js
用 CC 压缩成
http://lifesinger.github.com/lab/2010/cc-inline/test-cc-min.js

文件大小对比:

test.js           7.2K
test-cc-min.js    21.7K
test-yc-min.js    2.8K

看起来 CC 糟糕透了。不过看下 gzip 后的大小,心里平静很多:

test.js          1.2K
test-cc-min.js   750B
test-yc-min.js   1K

远没有想象中的糟糕。

CC 常量内联的代价

从下载传输量上看,即便像上面这样如此极端的代码,CC 的常量内联也没增加下载量,甚至能减少下载量。但从浏览器内存占用上看:

浏览器 ungzip 后,耗费的内存差异还是非常明显的。CC 的常量内联碰上变态代码时,还是会给浏览器带来一点小杯具的-.-

一切皆权衡

在 Google Closure Compiler 的 答记者问 里,有个挺有意思的回答:

Q: Does the compiler optimize for speed?

A: In most cases smaller code is faster code, since download time is usually the most important speed factor in web applications. Optimizations that reduce redundancies speed up the run time of code as well.

显然,对于常量内联来说,更多的是为了提高代码运行时的速度。因为有 gzip 保驾护航,常量内联并不会增加多少下载时间。对浏览器内存占用的增加,非极端情况下,一般也不会成为问题。

写在最后

从上面看起来,var SELECTED = 'selected'; 这种写法是没必要的。
从压缩角度讲,的确没必要。

不过从代码维护角度讲,还是非常有必要的。
这样可以避免将某处写成 'seleted', 直到调试了半天才发现。

一切皆权衡。