20

如果我给Closure Compiler这样的东西:

window.array = '0123456789'.split('');

它将它“编译”成这样:

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

现在你可以说,那更大了。Closure Compiler 这样做有什么原因吗?

4

3 回答 3

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)

编译器会将您尝试拆分的字符替换为它认为有利的字符。它通过迭代字符串的字符并找到字符串中没有出现的最有利的拆分字符来实现:

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

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

我相信在要拆分的字符串是空字符串的情况下插入逗号可能只是一个疏忽。这种情况似乎没有任何特殊处理,因此它与任何其他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

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


更新

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

上面我提到过几次霍夫曼编码。哈夫曼编码算法,解释得很简单,为要编码的文本中出现的每个字符分配一个权重。权重基于每个字符出现的频率。这些频率用于构建二叉树,最常见的字符位于根部。这意味着最常见的字符可以更快地解码,因为它们更接近树的根。

并且由于 Huffman 算法是 gzip 使用的 DEFLATE 算法的很大一部分。因此,如果您的 Web 服务器配置为使用 gzip,您的用户将受益于这种巧妙的优化。

于 2012-04-18T14:37:33.840 回答
5

此问题已于 2012 年 4 月 20 日修复,请参阅修订版: https ://code.google.com/p/closure-compiler/source/detail?r=1267364f742588a835d78808d0eef8c9f8ba8161

于 2012-04-24T15:49:45.227 回答
4

具有讽刺意味的是,split在编译代码中与源代码无关split。考虑:

Source  : a = ["0","1","2","3","4","5"]
Compiled: a="0,1,2,3,4,5".split(",")

这里,split只是表示长数组的一种方法(足够长,所有引号 + 逗号的总和比 长split(",""))。那么,您的示例中发生了什么?首先,编译器看到一个应用于常量的字符串函数并立即对其求值:

'0123456789'.split('') => ["0","1","2","3","4","5","6","7","8","9"]

稍后,当生成输出时,编译器认为这个数组是“长的”,并以上面的“拆分”形式写入:

["0","1","2","3","4","5","6","7","8","9"] => "0,1,2,3,4,5,6,7,8,9".split(",")

请注意,此时有关split('')源中的所有信息都已丢失。

如果源字符串较短,它将以数组数组形式生成,无需额外拆分:

Source  : a = '0123'.split('')
Compiled: a=["0","1","2","3"]
于 2012-04-18T14:59:48.847 回答