【问题标题】:How can I rewrite this so that Closure Compile renames my function?我怎样才能重写它,以便 Closure Compile 重命名我的函数?
【发布时间】:2013-06-07 00:15:56
【问题描述】:

我正在使用 Google Closure Compile 使用 ADVANCED_OPTIMIZATIONS 编译以下代码:

(function() {
  /** @const */
  var DEBUG = false;

  var Namespace = {};
  window['Namespace'] = Namespace;

  (function() {
     /**
      * @constructor
      */
    function Test(tpl) {
      Helper.debug('Test');
    }

    Namespace['Test'] = Test;

  })();

  var Helper = 
    (function(){
       /**
        * @constructor
        */
      function Helper(){
        this.debug = function(arg){
          if(DEBUG){
            console.log(arg);
          }
        }
      };

      return new Helper;
    })();

})();

我的目的是让编译器在 DEBUG == false 时删除所有 Helper.debug 消息,并在 DEBUG == true 时将调试函数重命名为短名称。我希望编译器能得到这样的结果:

DEBUG == false:

var a={};window.Namespace=a;a.Test=function(){};

DEBUG == true:

var a={};window.Namespace=a;a.Test=function(){console.log("Test")};

我最终得到了这个:

DEBUG == false:

 var a={};window.Namespace=a;a.Test=function(){b.debug("Test")};var b=new function(){this.debug=function(){}};

DEBUG == true:

var a={};window.Namespace=a;a.Test=function(){b.debug("Test")};var b=new function(){this.debug=function(c){console.log(c)}};

在这两种情况下,debug 函数都没有重命名。我认为它应该是,因为它没有被导出,也不能从Namespace 访问(据我所知)。它只能从 Namespace.Test() 构造函数中调用。如果我不从那里调用它,Closure 会剥离调试函数(因为它没有在任何地方使用),但我希望能够通过命名空间中的函数调用它,并且仍然对其进行重命名。

我已经尝试了上述代码的各种版本。在Helper 上使用prototype.debug,将Helper 构造函数移动到与命名空间相同的范围等。只要将调试函数附加到我的Helper 对象,我就找不到从编译器。

如果我不使用 Helper 对象,而只是将 debug 声明为一个函数,我会得到我想要的输出,但这只是一个示例,我确实有许多附加到 Helper 对象的函数,并且我希望将它们全部重命名为短名称。给出我想要的输出的示例代码:

(function() {
  /** @const */
  var DEBUG = false;

  var Namespace = {};
  window['Namespace'] = Namespace;

  (function() {
     /**
      * @constructor
      */
    function Test(tpl) {
      debug('Test');
    }

    Namespace['Test'] = Test;

  })();

  function debug(arg){
    if(DEBUG){
      console.log(arg);
    }
  }

})();

【问题讨论】:

  • 您可以使用 goog.DEBUG 获得条件代码; if(goog.DEBUG){.. 设置编译器时未编译代码 --define goog.DEBUG=false ...}
  • 我不确定闭包编译器是否适用于以您创建它们的方式创建的对象。如果使用 var Helper = function(){};Helper.prototype.debug=function(){} 定义 helper,调试会被重命名吗?或: var Helper ={debug:function(){}} 您的模式看起来像是用于通过闭包创建私有变量的东西,但在闭包库代码中的任何地方都看不到,他们会使用 this.private_ 和 @private 注释。
  • 嗨@HMR 感谢您的意见。我不知道good.DEBUG,但我有许多其他附加到 Helper 的函数,我想将它们重命名为短名称,但没有一个函数被重命名。我尝试了其他两种初始化 Helper 的方法,但我仍然遇到同样的问题。
  • 我建议你用@define developers.google.com/closure/compiler/docs/js-for-compiler 替换一些常量.然后编译器将删除死掉的代码。基本上goog.DEBUG 是您使用编译器选项更改的常量之一。
  • 更新了答案,debug 未重命名的原因是因为它在默认的 externs 文件中使用(其中定义了 document、window、document.getElementById ...)

标签: javascript google-closure-compiler


【解决方案1】:

我尝试了您的代码,发现名称debug 没有被转换,但其他名称会被转换。用 alert 替换了 console.log,因为我没有 console.log 的 externs 文件。这是您修改后的代码(仅将 debug 重命名为“something”并将 console.log 重命名为 alert):

(function() {
  /** @const */
  var DEBUG = false;

  var Namespace = {};
  window['Namespace'] = Namespace;

  (function() {
     /**
      * @constructor
      */
    function Test(tpl) {
      Helper.something('Test');
    }

    Namespace['Test'] = Test;

  })();

  var Helper = 
    (function(){
       /**
        * @constructor
        */
      function Helper(){
        this.something = function(arg){
          if(DEBUG){
            alert(arg);
          }
        }
      };

      return new Helper;
    })();

})();

现在编译你的代码或多或少有预期的输出(不使用构造函数):

java -jar compiler.jar --js helper.js --js_output_file out.js --compilation_level=ADVANCED_OPTIMIZATIONS --formatting=PRETTY_PRINT --warning_level=VERBOSE

(function() {
  var a = {};
  window.Namespace = a;
  (function() {
    a.Test = function() {
    }
  })()
})();

将 DEBUG 设置为 true 给我:

(function() {
  var a = {};
  window.Namespace = a;
  (function() {
    a.Test = function() {
      b.a()
    }
  })();
  var b = function() {
    return new function() {
      this.a = function() {
        alert("Test")
      }
    }
  }()
})();

请注意,引用的属性不会被重命名。所以使用this['something'] = function(arg){Helper['something']('Test'); 会导致某些东西不能被重命名。我猜你已经知道了,因为你使用的是 window['namespase']['Test']

[更新]

闭包编译器不会重命名外部定义的方法。例如,“document”是在编译器使用的 extern 中定义的(如果不是,那么每次使用 document 都会导致错误)。因此,如果您要将 Helper.debug 重命名为 Helper.getElementById,它仍然不会重命名它(getElementById 在编译器默认使用的 externs 文件中定义)。以下是 Oreilly.Closure.The.Definitive.Guide.Sep.2010 第 391 页的来源:

var mystery = function(obj) {
alert(obj.max());
};

因为神秘没有任何类型信息,obj 可能是 内置的 Math 对象或 example.NumSet。因为它是 有可能数学被提供为神秘的论据, 编译器无法重命名 max() 方法。为此, 编译器采用一种保守的方法通过 never 重命名变量 如果变量也出现在外部变量中,则重命名它。这是其中之一 编译器不包含额外外部的主要原因 默认情况下(例如 contrib/ externs 中的文件):添加更多 externs 池的名称可能会不必要地减少 变量重命名由编译器完成。

即使您提供类型信息,编译器也不会在没有一些额外标志的情况下重命名 --use_types_for_optimization。您的代码仍然不会重命名调试,但这可能是因为您需要一个 typedef。将 debug 重命名为 myDebug 会导致函数被重命名。

Why does Closure Compiler not rename objects with certain names?

【讨论】:

  • 非常感谢 HMR!我尝试了很多方法来重写它,我什至尝试过Helper.test,但由于完全相同的原因没有重命名。感谢您为我回答这个问题所付出的所有努力。
  • 不客气。当我读完这本书时偶然发现了这些函数没有重命名的原因,所以我想我应该更新答案。
猜你喜欢
  • 2018-08-04
  • 2016-01-31
  • 1970-01-01
  • 2021-10-06
  • 1970-01-01
  • 2021-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多