Closure编译器:@enum,属性崩溃和JSC_UNSAFE_NAMESPACE

时间:2015-07-23 21:18:03

标签: javascript google-closure-compiler

初始情况

我有以下代码:

var NS = {};  // namespace

NS.myEnum = {
    foo: 1,
    bar: 2
};

var extendedNS = Object.create(NS);

extendedNS.myAlert = function (x) { alert(x); };

extendedNS.myAlert(extendedNS.myEnum.foo);

使用此命令成功编译(没有警告或错误):

java -jar compiler-20150609.jar --js test.js --compilation_level ADVANCED --warning_level VERBOSE

问题1

根据docs for JSC_UNSAFE_NAMESPACE,我认为高级优化可能会将NS.myEnum替换为NS$myEnum,然后移除NS

那么为什么编译器会对此行发出警告?

var extendedNS = Object.create(NS);

这不是对名称空间NS的不安全引用吗?不应该编译器警告:"为命名空间NS" 创建了不完整的别名?

@enum类型中断编译输出

我现在将NS.myEnum标记为枚举:

/**
 * @enum {number}
 */
NS.myEnum = {
    foo: 1,
    bar: 2
};

根据this old SO answer"编译器希望将枚举值折叠为单个变量" 。所以我认为编译器现在可以将NS.myEnum折叠为:

NS$myEnum$foo = 1;
NS$myEnum$bar = 2;

编译器现在发出警告:

WARNING - incomplete alias created for namespace NS
var extendedNS = Object.create(NS);
                               ^

我想我理解为什么:在折叠枚举值后,高级优化可能已删除NS

编译后的输出确实已损坏:

var a = Object.create({});
a.a = function() {
  alert(a.c.b);      // a.c.b doesn't exist, so a runtime error will occur
};
a.a();

问题2

我现在向枚举添加@nocollapse标记("表示编译器不应将其折叠为变量的属性。如果使用{注释属性为对象的属性{1}},其所有属性也将保持未折叠状态。" ):

@nocollapse

编译后的输出现在是有效代码:

/**
 * @enum {number}
 * @nocollapse
 */
NS.myEnum = {
    foo: 1,
    bar: 2
};

但是编译器仍然会发出var a = Object.create({c:{a:1, f:2}}); a.b = function() { alert(a.c.a); // a.c.a does exist }; a.b(); 警告:

JSC_UNSAFE_NAMESPACE

为什么呢?我需要在WARNING - incomplete alias created for namespace NS var extended = Object.create(NS); ^ 添加另一个标记来阻止此警告吗?

(注意:我不想NS.myEnum警告。我想了解并解决警告的原因。)

1 个答案:

答案 0 :(得分:1)

核心问题归结为传统上@enum属性一直被折叠 - 因此当@enum注释被添加为代码时警告会被破坏(并且在您的第一个示例中) )。

@nocollapse会阻止枚举折叠,并且当存在该注释时,可能会错误地发出警告。如果您愿意,可以file a bug关于此问题,但这可能是一个低优先级问题。

在一般情况下,enums被设计为更像常量,因此使用它们作为proto对象不是预期/支持的行为。

围绕财产崩溃的规则和案例很复杂。了解它们的最佳方法是查看unit tests以查看触发警告的特定模式。