Closure Compiler ADVANCED_OPTIMIZATIONS 和函数封装
使用Google Closure Compiler(ADVANCED_OPTIMIZATIONS),似乎当代码封装在函数中时,有某些高级优化无法完成。
(function(){
var db = {};
/** @enum {number} */
db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
alert(db.col.Two); alert(db.col.Three); alert(db.col.Four);
})();
var db = {};
/** @enum {number} */
db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
alert(db.col.Two); alert(db.col.Three); alert(db.col.Four);
编译为
var a={a:{f:0,d:3,c:4,b:1,e:2,h:7,g:8}};alert(a.a.d);alert(a.a.c);alert(a.a.b);
alert(3);alert(4);alert(1);
函数封装阻止高级变量替换的原因是什么?有没有办法做到这一点,使两个片段编译为相同的输出?
Using Google Closure Compiler (ADVANCED_OPTIMIZATIONS), it seems that when code is encapsulated in a function, there are certain advanced optimizations that cannot be done.
(function(){
var db = {};
/** @enum {number} */
db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
alert(db.col.Two); alert(db.col.Three); alert(db.col.Four);
})();
var db = {};
/** @enum {number} */
db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
alert(db.col.Two); alert(db.col.Three); alert(db.col.Four);
compiles to
var a={a:{f:0,d:3,c:4,b:1,e:2,h:7,g:8}};alert(a.a.d);alert(a.a.c);alert(a.a.b);
alert(3);alert(4);alert(1);
What is the reason that function encapsulation prevents the advanced variable substitution? Is there any way to do this such that both snippets compile to the same output?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
一种可能的解释:
您所指的闭包编译器的功能是“命名空间扁平化”,这是编译器试图规避与长命名空间链中的查找相关的成本。
例如,
foo.bar.baz.hello.doSomething();
需要导航四个对象链才能找到doSomething
属性。通过命名空间扁平化,属性被扁平化为a
,并且调用被a();
取代——这是一个重大改进。因此,在第二种情况下,真正有问题的并不是对象
db
。我相信会发生以下优化链:命名空间扁平化:
然后,由于 b、c、d 都只使用一次,因此它们是内联的:
最后,丢弃未使用的变量 a、e、f、g。
然而,尽管这在全局范围内工作得很好,但当对象在闭包内定义时,编译器必须格外小心,因为该闭包内可能存在捕获该闭包内定义的对象的函数调用。闭包内的所有内容都必须是“无副作用”的,以便编译器“展平”对象并消除对象;否则,如果内部函数调用引用的捕获对象不再存在,代码将中断。
alert()
不被认为是没有副作用的。因此,假设可以通过调用alert
来修改db
和db.col
。之后任何可能存在副作用的代码都可以引用修改后的 db 或 db.col,因此不得消除这些对象。 注意:如果alert()
调用是最后一个非无副作用的调用,则此规则不适用。要启用命名空间扁平化,您必须将闭包外部的对象并在全局范围内定义它们,这是无法捕获的:
这将起作用:
一个很好的实验是:
瞧!它有效:
但是:
不有效吗:
One possible explanation:
The feature of the Closure Compiler you are referring to is "namespace flattening", which is the compiler's attempt to circumvent costs related to lookups in long chains of namespaces.
For example,
foo.bar.baz.hello.doSomething();
requires navigating a chain of four objects to find thedoSomething
property. With namespace flattening, the property is flattened toa
, and the call is replaced bya();
-- a significant improvement.Therefore, in your second case, it is not really the object
db
that is at issue. I believe the following chain of optimizations happens:Flattening of namespace:
Then, since b,c,d are all used only once, they are in-lined:
Then finally, the unused variables a,e,f,g are discarded.
However, although this works fine in the global scope, the compiler must be extra careful when objects are defined inside a closure, because there may be function calls inside that closure that captures objects defined within that closure. Everything inside the closure must be "side-effect-free" in order for the compiler to "flatten" the objects and to eliminate the objects; otherwise code will break if the captured objects that an internal function call refers to are no longer there.
alert()
is not assumed to be side-effect free. Therefore, it is assumed thatdb
anddb.col
may be modified by the call toalert
. Any code afterwards that is potentially not side-effect-free can refer to the modifieddb
ordb.col
, so these objects must not be eliminated. Note: this does not apply if thealert()
call is the last call that is non-side-effect-free.To enable namespace flattening, you have to move the objects outside of the closure and define them in the global scope, which cannot be captured:
This will work:
One good experiment is:
Lo and behold! It works:
However:
does NOT work: