为什么Closure Compiler坚持添加更多字节?

qwe*_*ymk 20 javascript minify google-closure-compiler

如果我给Closure Compiler这样的话:

window.array = '0123456789'.split('');
Run Code Online (Sandbox Code Playgroud)

它"编译"它:

window.array="0,1,2,3,4,5,6,7,8,9".split(",");
Run Code Online (Sandbox Code Playgroud)

现在你可以说,这更大.Closure Compiler是否有任何理由这样做?

Jam*_*ice 21

认为这是正在发生的事情,但我不确定......

导致插入逗号的代码tryMinimizeStringArrayLiteral位于PeepholeSubstituteAlternateSyntax.java中.

该方法包含可能具有低霍夫曼编码的字符列表,因此比其他字符更适合拆分.如果你尝试这样的话,你可以看到这个的结果:

"a b c d e f g".split(" "); //Uncompiled, split on spaces
"a,b,c,d,e,f,g".split(","); //Compiled, split on commas (same size)
Run Code Online (Sandbox Code Playgroud)

编译器将用你认为有利的字符替换你试图拆分的字符.它通过迭代字符串的字符并找到字符串中不存在的最有利的拆分字符来实现:

// These delimiters are chars that appears a lot in the program therefore
// probably have a small Huffman encoding.
NEXT_DELIMITER: for (char delimiter : new char[]{',', ' ', ';', '{', '}'}) {
  for (String cur : strings) {
    if (cur.indexOf(delimiter) != -1) {
      continue NEXT_DELIMITER;
    }
  }
  String template = Joiner.on(delimiter).join(strings);
  //...
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段中,您可以看到编译器声称最适合拆分的字符数组.逗号是第一个(这就是为什么在上面的空间示例中,空格已被逗号替换).

我相信在要拆分的字符串是空字符串的情况下插入逗号可能只是一个疏忽.似乎没有对这种情况进行任何特殊处理,因此它被视为任何其他split调用,并且每个字符都与上面代码段中显示的数组中的第一个相应字符连接在一起.


编译器如何处理该split方法的另一个例子:

"a,;b;c;d;e;f;g".split(";"); //Uncompiled, split on semi-colons
"a, b c d e f g".split(" "); //Compiled, split on spaces
Run Code Online (Sandbox Code Playgroud)

这一次,因为原始字符串已经包含逗号(并且我们不想在逗号字符上拆分),所以不能从低霍夫曼编码的字符数组中选择逗号,因此下一个最佳选择是选中(空间).


更新

在对此进行进一步研究之后,它绝对不是一个错误.这种行为实际上是设计的,在我看来,这是一个非常聪明的小优化,当你记住Closure编译器倾向于支持编译代码的速度超过大小.

上面我提到了霍夫曼编码几次.非常简单地解释的霍夫曼编码算法为出现在要编码的文本中的每个字符分配权重.权重取决于每个角色出现的频率.这些频率用于构建二叉树,其中最常见的字符位于根.这意味着最常见的字符可以更快地解码,因为它们更接近树的根.

由于霍夫曼算法是gzip使用的DEFLATE算法的很大一部分.因此,如果您的Web服务器配置为使用gzip,您的用户将从这个聪明的优化中受益.