使用闭包编译器简单优化从函数内部删除调试代码

Removing debug code from inside a function using Closure Compiler simple optimisations

本文关键字:内部 函数 删除 调试 代码 优化 闭包 编译器 简单      更新时间:2023-09-26

我正在寻找一种从函数中删除调试代码的方法,以便我可以将测试钩子添加到闭包中。 我读过Google闭包编译器高级:在编译时删除代码块,并使用以下方法测试删除调试代码:

/** @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(){};

是否有原因无法按预期工作? 我真的只是在寻找一种干净的方法来剥离调试代码,并且还没有准备好投入高级优化。

更新

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

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. 由于值是在文件开头定义的,因此无法灵活地事先接收值。

我已经考虑过从文件中删除所有var DEBUG定义并在执行前将其注入源或外部定义的想法,但我遇到了两个问题:

  • 在 extern 中定义它似乎没有任何作用。
  • 编译代码中未定义的DEBUG会在浏览器中引发引用错误。

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

除非另一个编译器选项有效,否则唯一真正有意义的选项是使用 window.DEBUG 并在编译注入/** @const */ var DEBUG = false;之前将 /'bwindow.DEBUG'b/ 全局替换为 DEBUG . 有没有更好的方法?

使用@define注释:

@define {boolean}

调试 = 真;

并使用选项进行编译

-

-define="DEBUG=false">

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

options.setInlineConstantVars(true(;

您可以在此处添加它,在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

这是一个古老的答案,但我找到了一种这里没有提到的方法。

(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 可以看到我们不能做类似 window.DEBUG === true 的事情,因此可以保证代码永远不会运行。

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

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

/** @const 
*   @type {boolean}
*/
var DEBUG = false;
//and used this format for my debug function
DEBUG && myLog('foo'); 

然后使用 ant 编译闭包编译器 java 以包含此选项 options.setInlineVariables(CompilerOptions.Reach.ALL(; 在编译级别.java文件中的应用安全编译选项功能下,如@john建议的那样。这对我有用,并没有像 ADVANCED 那样破坏我的代码库......

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