使用Closure Compiler简单优化从函数内部删除调试代码

时间:2012-07-09 20:01:25

标签: javascript google-closure-compiler minify

我正在寻找一种从函数中删除调试代码的方法,这样我就可以在闭包中添加测试挂钩。我读了 Google Closure Compiler advanced: remove code blocks at compile time并测试了以下内容删除调试代码:

/** @define {boolean} */
var DEBUG = true;

if (DEBUG) {
    console.log('remove me');
}

使用--define='DEBUG=false'进行简单优化会将此优化减少到var DEBUG=!1;。这同样适用于此:

/** @const */
var DEBUG = false;

if (DEBUG) {
    console.log('remove me');
}

我遇到麻烦的地方是在函数中使用这个约定:

/** @const */
var DEBUG = false;

function logMe() {
    if (DEBUG) {
        console.log('remove me');
    }
}

这减少到以下几点:

var DEBUG=!1;function logMe(){DEBUG&&console.log("remove me")};

我希望它能进一步减少到:

var DEBUG=!1;function logMe(){};

有没有理由不按预期工作?我真的只是在寻找一种简单的方法来剥离调试代码,而我还没有准备好接受高级优化。

更新

Per @ John的回答,我实现了自己的编译器,并发现以下配置将从代码内部和外部删除if (DEBUG) {} @define的情况:

CompilerOptions options = new CompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
//options.setInlineConstantVars(true);
options.setInlineVariables(CompilerOptions.Reach.ALL);
options.setDefineToBooleanLiteral("DEBUG", false);

这对于具有以下限制的单个文件非常有效:

  1. 这需要在每个文件中定义var DEBUG,这是不好的做法。
  2. 组合多个文件时,您只能拥有一个var DEBUG,或者编译器无法对其进行优化。这可以通过单独编译每个文件并合并它们来避免。
  3. 由于该值是在文件开头定义的,因此无法预先接收该值。
  4. 我已经玩弄了从文件中删除所有var DEBUG定义并在执行之前将其注入源或extern的想法,但我遇到了两个问题:

    • 在extern中定义它似乎什么都不做。
    • 未编译代码中未定义的DEBUG会在浏览器中引发引用错误。

    理想的选择是测试window.DEBUG,它不会引发参考错误。不幸的是,虽然注入/** @const */ var window = {}; /** @const */ window.DEBUG = false;在顶层工作,减少if (window.DEBUG) {},但如果放在函数中,优化实际上会被恢复。

    除非另一个编译器选项有效,否则唯一有意义的选择是使用window.DEBUG并在编译之前注入/** @const */ var DEBUG = false;并使用/\bwindow.DEBUG\b/进行全局替换DEBUG }。还有更好的方法吗?

6 个答案:

答案 0 :(得分:3)

编译器的自定义构建允许您执行此操作。你基本上想要“内联常量变量”:

options.setInlineConstantVars(真);

您可以在applySafeCompilationOptions中添加它: http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/CompilationLevel.java?r=706

或者您可以使用Java API并添加选项(无需修改编译器的代码)。 Michael Bolin在这里给出了一个如何做到这一点的例子:

http://blog.bolinfest.com/2009/11/calling-closure-compiler-from-java.html

答案 1 :(得分:3)

使用@define注释:

@define {boolean}

DEBUG = true;

使用选项

进行编译

- 限定= “DEBUG =假”

答案 2 :(得分:2)

这是一个陈旧的答案,但我发现了一种在这里没有提到的方法。

(function(){
    var DEBUG = true;

    if (DEBUG) {
        if (something === "wrong") {
            console.warn("Stop!  Hammer time!");
        }
        else if (something === "as expected") {
            console.log("All good :-)");
        }
    }

    foo();
})();

使用ADVANCED_OPTIMIZATIONS,可以编译:

"wrong" === something ? 
    console.warn("Stop!  Hammer time!") : 
    "as expected" === something && console.log("All good :-)");
foo();

在我们的构建脚本中,我们可以重写DEBUG行,将其设置为false,然后产生此输出。

foo();

发生这种情况的原因是Closure将删除无法访问的代码。通过创建闭包并定义局部变量,Closure可以看到我们不能执行window.DEBUG === true之类的操作,因此保证代码永远不会运行。

答案 3 :(得分:1)

您的DEBUG变量目前是全局的。 GCC不会在简单优化模式下删除或重命名全局变量,因此它们仍然可用于可能想要访问它们的其他脚本中的任何代码。尝试将代码封装到匿名函数中。

答案 4 :(得分:1)

我解决了“使用SIMPLE_OPTIMIZATION从关闭编译的javascript中删除调试函数”的问题的方法是结合@John提出的类似方法以及使用一些@Brian Nichols更新。我只能让编译器通过放置这是我的主js文件的全局范围并进行自定义编译来删除行(使用多个.js文件,这仍然删除它们)

/** @const 
*   @type {boolean}
*/
var DEBUG = false;

//and used this format for my debug function
DEBUG && myLog('foo'); 

然后使用ant编译closure-compiler java以包含此选项options.setInlineVariables(CompilerOptions.Reach.ALL);  在@john建议的CompilationLevel.java文件中的applySafeCompilationOptions函数下。这对我有用,并没有像ADVANCED那样打破我的代码库...

答案 5 :(得分:0)

从您的代码中移除var DEBUG = true;,并将检查if (DEBUG)的所有条件转换为if (goog.DEBUG)。修改编译器选项以读取--define goog.DEBUG=falsegoog变量内置于Closure Library API中,为编译器提供选项和标志。