简体   繁体   English

使用Google Closure编译器防止将导出的属性内联为常量

[英]Preventing inlining of exported properties as constants w/ Google Closure Compiler

Here's a minimal example of input JavaScript (generated by another language, but that's neither here nor there): 这是输入JavaScript的最小示例(由另一种语言生成,但这既不是这里也不是那里):

goog.provide('foo.bar');
foo.bar.baz = 42;
goog.exportSymbol('foo.bar.baz', foo.bar.baz);
foo.bar.quux = (function quux(){return foo.bar.baz;
});
goog.exportSymbol('foo.bar.quux', foo.bar.quux);

My expectation is that, because foo.bar.baz is not annotated as a constant, that it would not be treated as one. 我的期望是,因为foo.bar.baz未被注释为常量,所以不会将其视为一个常量。 Yet, advanced optimizations (both locally and via the compiler service ) inlines it anyway: 但是,无论如何,高级优化(无论是在本地还是通过编译器服务 )都可以内联:

var d = this;
function f(g, e) {
  var b = g.split("."), a = d;
  b[0] in a || !a.execScript || a.execScript("var " + b[0]);
  for (var c;b.length && (c = b.shift());) {
    b.length || void 0 === e ? a = a[c] ? a[c] : a[c] = {} : a[c] = e;
  }
}
;f("foo.bar.baz", 42);
f("foo.bar.quux", function() {
  return 42;
});

Constant inlining is great, but the value in question isn't safe to inline, given that it's exported. 常量内联非常好,但是考虑到已导出的值,因此内联的值并不安全。 I've tried using goog.exportProperty and @expose annotations in addition to and instead of goog.exportSymbol , with no luck. 我已经尝试使用goog.exportProperty@expose注释来代替goog.exportSymbol ,并且没有运气。

Help? 救命? Thanks! 谢谢!

Update: @expose is now deprecated. 更新: @expose现在已弃用。 Answer updated with new method. 答案已使用新方法更新。

As you noted, @const has no impact on inlining. 如您所述, @const联没有影响。 @const only means that the value doesn't change (is assigned/set only once). @const仅表示该值不变(仅分配/设置一次)。

@nocollapse was specifically created to prevent property collapsing. @nocollapse是专门为防止属性崩溃而创建的。 Given: 鉴于:

goog.provide('foo.bar');
/** @nocollapse */
foo.bar.baz = 42;
alert(foo.bar.baz);

The purpose of @nocollapse in this case is to prevent the compiler from collapsing the property to something like: 在这种情况下, @nocollapse的目的是防止编译器将属性折叠为类似以下内容:

var a = 42;
alert(a);

However the compiler still knows from flow analysis that foo.bar.baz is assigned the integer 42 and never changed. 但是,编译器仍从流分析中得知foo.bar.baz被分配了整数42并且从未更改。 Therefore it's going to just use the known values. 因此,它将仅使用已知值。

The only reliable way to prevent inlining is to export the property: 防止内联的唯一可靠方法是导出属性:

goog.provide('foo');
goog.provide('foo.bar');
foo.bar.baz = 42;
goog.exportProperty(foo.bar, 'baz', foo.bar.baz);
alert(foo.bar.baz);

There is a known (albeit small) need to stipulate to the compiler that renaming is safe, but inlining is not. 已知(尽管很小)需要向编译器规定重命名是安全的,但内联不是。 See https://code.google.com/p/closure-compiler/issues/detail?id=971 请参阅https://code.google.com/p/closure-compiler/issues/detail?id=971

If you are not using closure library, the export would look like this: 如果您不使用闭包库,则导出将如下所示:

var foo = {};
foo.bar = {};
/** @nocollapse */
foo.bar.baz = 42;
foo.bar['baz'] = foo.bar.baz;
alert(foo.bar.baz);

If your goal is to make sure foo.bar.baz isn't inlined OR renamed, then you would need either quoted properties and @nocollapse . 如果您的目标是确保不内联或重命名foo.bar.baz ,那么您将需要带引号的属性和@nocollapse

goog.provide('foo');
goog.provide('foo.bar');
/** @nocollapse */
foo.bar.baz = 42;
goog.exportProperty(foo.bar, 'baz', foo.bar.baz);
alert(foo.bar.baz);

If you are providing this namespace to others for use, then the best case is a combination of @nocollapse to prevent collapsing and allow external updating and goog.exportSymbol . 如果您要将此命名空间提供给他人使用,那么最好的情况是@nocollapse的组合,以防止崩溃并允许外部更新和goog.exportSymbol

goog.provide('foo');
goog.provide('foo.bar');
/** @nocollapse */
foo.bar.baz = 42;
goog.exportSymbol('foo', foo);
goog.exportProperty(foo, 'bar', foo.bar);
goog.exportProperty(foo.bar, 'baz', foo.bar.baz); 

alert(foo.bar.baz);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM