【问题标题】:Annotate Singleton objects in JavaScript for the Google Closure Compiler, or "dangerous use of the global this object" warning在 JavaScript 中为 Google Closure Compiler 注释 Singleton 对象,或“危险使用全局 this 对象”警告
【发布时间】:2011-08-06 22:55:36
【问题描述】:

我正在使用 ADVANCED_OPTIMIZATIONS 编译级别的 Google Closure Compiler 并已开始注释我的构造函数,因为我收到了各种警告:

警告 - 危险地使用全局 this 对象

对于我的“构造函数”类型的函数,我会这样注释它们:

/**
 * Foo is my constructor
 * @constructor
 */
Foo = function() {
   this.member = {};
}

/**
 * does something
 * @this {Foo}
 */
Foo.prototype.doSomething = function() {
   ...
}

这似乎工作正常,但是如果我有一个不是用 var myFoo = new Foo(); 构造​​的“单例”对象怎么办? 我在文档中找不到如何注释这种类型的对象,因为它的类型只是对象吗?

Bar = {
   member: null,
   init: function() {
      this.member = {};
   }
};

【问题讨论】:

    标签: javascript google-closure-compiler jsdoc


    【解决方案1】:

    正是“危险使用此”警告您注意的潜在错误类型。在您的示例中,闭包编译器可能尝试将您的代码“扁平化”为:

    Bar$member = null;
    Bar$init = function() { this.member = {}; };
    

    注意:闭包编译器目前不会展平声明为全局对象的命名空间(即前面没有“var”关键字),因此您的代码现在可能仍然有效。但是,没有人知道它在未来的版本中不会这样做,并且您的代码会突然中断而没有警告。

    当然,那么“Bar$member”和“Bar$init”将分别重命名为“a”和“b”。这称为“命名空间扁平化”或“属性折叠”。

    您可以立即看到您的代码不再正常工作。编译前,如果你写:

    Bar.init();
    

    this 将引用Bar。但是编译后就变成了:

    Bar$init();
    

    this 将不再指代Bar。相反,它指的是全局对象。

    这是编译器试图警告您以这种方式使用“this”是“危险”的方式,因为“this”可能会更改为引用“全局”对象。这才是警告的真正含义。

    简而言之,不要这样做。这种编码风格会产生非常难以追踪的错误。

    以这种方式修改您的代码:

    var Bar = {    // Closure Compiler treats globals and properties on global differently
      member: null,
      init: function() { Bar.member = {}; }
    };
    

    或使用闭包:

    var Bar = (function() {
      var member = null;
      return {
        init: function() { member = {}; }
      };
    })();
    

    在高级模式下使用闭包编译器时,不要试图通过注释掉警告来消除它们。警告是有原因的——它们试图警告你一些事情。

    【讨论】:

    • 谢谢,在内部引用单例名称而不是这样可以防止编译器压平我的单例,这是有道理的。
    • 好吧,@Matt Palmerlee,不是那样的。引用命名空间(即单例)名称而不是使用“this”可以避免编译器扁平化单例时出现错误。换句话说,编译后,您的“单例”对象不再存在。但是,正如我所写的,这仅适用于您的命名空间以“var”为前缀的情况。否则,您的命名空间将不会被展平,并且您的程序将继续工作,即使您没有消除“危险的这个”警告。
    • 感谢@Stephen Chung 的澄清,我没有使用 var 语法,因为我的代码比我的简单示例复杂一点,我有一个全局应用程序“命名空间”对象,即 Bar 单例是的成员,所以我声明它像: var AppNS = {}; AppNS.Bar = {...};无论哪种方式,我都继续在单例中使用全名 AppNS.Bar ,这样警告就不会出现,而且它会更有未来的证明。谢谢。
    • @Matt,太好了。做一个var AppNS = {}; 就足够了。这使得整个命名空间成为一个全局属性,而不是一个全局对象。闭包不对全局对象做任何优化。
    【解决方案2】:

    在 Closure 中创建单例的首选方式是这样的:

    /** @constructor */
    var Bar = function() { };
    goog.addSingletonGetter(Bar);
    
    Bar.prototype.member = null;
    
    Bar.prototype.init = function() {
      this.member = {};
    };
    

    这允许对单例进行延迟实例化。像这样使用它:

    var bar1 = Bar.getInstance();
    var bar2 = Bar.getInstance();
    
    bar1.init();
    console.log(bar2.member);
    

    请记住,这不会阻止人们使用构造函数来创建 Bar 的实例。

    【讨论】:

    • 谢谢你,但是我根本没有在我的 js 中使用闭包库,只是使用闭包编译器来缩小和(有点)混淆代码。我是否需要通过库才能访问诸如 addSingletonGetter 之类的方法才能实现此目的?我喜欢我的单例对象现在是多么干净,我宁愿不必为了让编译器高级模式为它们工作而破坏它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    • 2016-03-12
    • 2016-07-06
    • 1970-01-01
    • 2017-05-26
    相关资源
    最近更新 更多