【问题标题】:Closure compiler doesn't remove unused properties闭包编译器不会删除未使用的属性
【发布时间】:2016-04-21 12:28:05
【问题描述】:
(function(){
    var num = 4 // Math.random()
    var module1 = {};

    (function(export_to){
        export_to.add1 = function(arg) { return arg+1 }
        export_to.add2 = function(arg) { return arg+2 }
        export_to.add10 = function(arg) { return arg+10 }
    })(module1)

    console.log(module1.add10(num))
})()

这是原始的内联“模块”示例。只要num 不变,GCC 就会正确内联add10 函数并用--compilation_level=ADVANCED 消除其他所有内容,只留下:

console.log(14);

结果。

// 更改为 && 虽然会使 GCC 的行为变得更糟。由于某种原因,它不再内联.add10,也不再注意到.add1.add2 根本没有被调用,实际上是死代码。

(function(){var d=Math.random(),c={};(function(b){b.b=function(a){return a+1};b.c=function(a){return a+2};b.a=function(a){return a+10}})(c);console.log(c.a(d))})();

有没有办法让 GCC 消除/内联此类函数,同时仍将它们保留在自己的 function 范围内,以防我需要一些私有持久变量并整齐地分组在原始源中的对象中?也许是一些注释?

这一切都在发布时在最新可用的预构建版本上进行了测试:

Closure Compiler (http://github.com/google/closure-compiler)
Version: v20160315
Built on: 2016/03/25 11:43

【问题讨论】:

  • 你可能需要找到注解来给编译器更多的信息,但我想不出该怎么做。闭包编译器更倾向于classical Java-style inheritance patterns。这种编写模块的方式似乎是 Closure Compiler 的“新前沿”。见github.com/google/closure-compiler/wiki/JS-Modules。我希望对 Closure Compiler 中这种“模块样式”有更多经验的人可以帮助您。
  • 在下面的回答中,我表明最新版本的 Closure Compiler 可以解决您的问题。我不知道他们什么时候会制作另一个预构建的编译器,但是在本地机器上构建编译器很容易。步骤是:git clone https://github.com/google/closure-compiler.gitant jar
  • 您还需要回答这个问题吗?

标签: javascript google-closure-compiler


【解决方案1】:

更新:最新版本的 Closure Compiler 正确地缩小了您的代码。 online Closure Compiler 还是老版本(不知道是什么版本)。

这显示了我正在使用的编译器的版本

$ java -jar ../javascript/closure-compiler/build/compiler.jar --version
Closure Compiler (http://github.com/google/closure-compiler)
Version: v20160315-213-g4787bb5
Built on: 2016/04/25 10:12

这显示了我正在编译的代码:

$ cat test2.js
(function(){
    var num = Math.random();
    var module1 = {};

    (function(export_to){
        export_to.add1 = function(arg) { return arg+1 }
        export_to.add2 = function(arg) { return arg+2 }
        export_to.add10 = function(arg) { return arg+10 }
    })(module1)

    console.log(module1.add10(num))
})()

这显示了编译命令和结果:

$ java -jar ../javascript/closure-compiler/build/compiler.jar 
    --js test2.js --compilation_level ADVANCED
console.log(Math.random()+10);

以下是我之前的回答(现在无关紧要)。


您遇到了 Closure Compiler 的一些限制。这是来自Closure Compiler Issue 891: missed property collapsing opportunity的引用

问题是“属性”折叠仅在全局范围内发生一次,即在函数内联发生之前(在函数内,这发生在优化循环期间)。为此,“折叠属性”需要至少再发生一次,或者我们需要增强功能本地版本以便能够在全局范围内运行。

您需要修改 DefaultPassConfig 才能再次运行 CollapseProperties。没有现有的编译器选项可以执行此操作。

因此,可能有一些方法可以修改 Closure Compiler 来处理您的情况。这是一篇关于如何开始修改编译器的博客文章:High-level overview of a compilation job

另见Understanding Property Removal

我不清楚您的要求,您似乎使用的是module pattern。如果您不需要该特定模式,则此代码有效:

(function() {
var Adder = {};
Adder.add1 = function(arg) { return arg+1; }
Adder.add2 = function(arg) { return arg+2; }
Adder.add10 = function(arg) { return arg+10; }

console.log(Adder.add10(Math.random()));
})()

使用带有高级优化的online Closure Compiler 的结果很好地缩小了

console.log(Math.random()+10);

但是使用增强模块模式并没有完全折叠代码,尽管它确实做了一些内联。这是一个例子:

var module1 = {};
module1 = (function(export_to){
    export_to.add1 = function(arg) { return arg+1 }
    export_to.add2 = function(arg) { return arg+2 }
    export_to.add10 = function(arg) { return arg+10 }
    return export_to;
})(module1);

console.log(module1.add10(4))

高级优化的结果是

var b={},b=function(a){a.a=function(a){return a+1};a.c=function(a)
{return a+2};a.b=function(){return 14};return a}(b);console.log(14);

这可能相当于bug in the compiler

我怀疑您的原始代码崩溃到console.log(14) 有点侥幸。我的猜测是编译器在编译过程中做了一些整数运算,而这种情况恰好可以解决。请注意,使用非整数会导致您的原始代码不会崩溃(与使用 Math.random() 时的值相同)num

(function(){
    var num = 4/3;
    var module1 = {};

    (function(export_to){
        export_to.add1 = function(arg) { return arg+1 }
        export_to.add2 = function(arg) { return arg+2 }
        export_to.add10 = function(arg) { return arg+10 }
    })(module1)

    console.log(module1.add10(num))
})()

结果:

(function(){var d=4/3,c={};(function(b){b.b=function(a){return a+1};
b.c=function(a){return a+2};b.a=function(a){return a+10}})(c);
console.log(c.a(d))})();

【讨论】:

  • “顺便说一句,”我还提醒您在编写(或使用)这样的代码时“始终查看一般案例”。编译器可能“聪明”,但绝不是“智能”。此外:你告诉它“在任何时间”做的任何事情,它都注定要在“每次时间”做。例如,如果您“修改编译器”以按预期处理“case-X”,编译器将盲目地将相同的 mod 应用于“case-Y、Z 和 Q!”也许这就是为什么(!)一些作者......不仅选择不实现特定选项,而且明确选择这样说,“详细”。 . . . :-/
猜你喜欢
  • 2013-09-21
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多