Closure Compiler ADVANCED_OPTIMIZATIONS 和函数封装

发布于 2024-11-14 17:01:59 字数 667 浏览 5 评论 0原文

使用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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

柠檬心 2024-11-21 17:01:59

一种可能的解释:

您所指的闭包编译器的功能是“命名空间扁平化”,这是编译器试图规避与长命名空间链中的查找相关的成本。

例如,foo.bar.baz.hello.doSomething(); 需要导航四个对象链才能找到 doSomething 属性。通过命名空间扁平化,属性被扁平化为 a,并且调用被 a(); 取代——这是一个重大改进。

因此,在第二种情况下,真正有问题的并不是对象db。我相信会发生以下优化链:

var db = {};
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=0, b=3, c=4, d=1, e=2, f=7, g=8;
alert(b); alert(c); alert(d);

然后,由于 b、c、d 都只使用一次,因此它们是内联的:

var a=0, e=2, f=7, g=8;
alert(3);alert(4);alert(1);

最后,丢弃未使用的变量 a、e、f、g。

然而,尽管这在全局范围内工作得很好,但当对象在闭包内定义时,编译器必须格外小心,因为该闭包内可能存在捕获该闭包内定义的对象的函数调用。闭包内的所有内容都必须是“无副作用”的,以便编译器“展平”对象并消除对象;否则,如果内部函数调用引用的捕获对象不再存在,代码将中断。

alert() 不被认为是没有副作用的。因此,假设可以通过调用 alert 来修改 dbdb.col。之后任何可能存在副作用的代码都可以引用修改后的 db 或 db.col,因此不得消除这些对象。 注意:如果 alert() 调用是最后一个非无副作用的调用,则此规则不适用。

要启用命名空间扁平化,您必须将闭包外部的对象并在全局范围内定义它们,这是无法捕获的:

  1. 将对象定义移到函数闭包之外(因此使它们成为命名空间)
  2. 避免使用对象表示法

这将起作用:

var db = {};    // Put the namespace outside, making it global
db.col = {};    // Put sub-namespaces outside also

(function(){
    db.col.One = 0;    // Avoid using object notation
    db.col.Two = 3;
    db.col.Three = 4;
    db.col.Four = 1;
    db.col.Five = 2;
    db.col.Siz = 7;
    db.col.Seven = 8;

    alert(db.col.Two); alert(db.col.Three); alert(db.col.Four);
})();

一个很好的实验是:

(function() {
    var db = {};
    db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
    alert(db.col.Two);   // Only one call
    var test = db.col.Three + db.col.Four;   // This statement is side-effect-free
})();

瞧!它有效:

alert(3);

但是:

(function() {
    var db = {};
    db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
    alert(db.col.Two);     // First call, anything afterwards is suspect
    alert(db.col.Three);   // Oops!  Cannot eliminate db or db.col!
})();

有效吗:

var a={a:{f:0,c:3,b:4,e:1,d:2,h:7,g:8}};alert(a.a.c);alert(a.a.b);

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 the doSomething property. With namespace flattening, the property is flattened to a, and the call is replaced by a(); -- 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:

var db = {};
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);

Flattening of namespace:

var a=0, b=3, c=4, d=1, e=2, f=7, g=8;
alert(b); alert(c); alert(d);

Then, since b,c,d are all used only once, they are in-lined:

var a=0, e=2, f=7, g=8;
alert(3);alert(4);alert(1);

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 that db and db.col may be modified by the call to alert. Any code afterwards that is potentially not side-effect-free can refer to the modified db or db.col, so these objects must not be eliminated. Note: this does not apply if the alert() 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:

  1. Move the object definitions outside of the function closure (therefore making them namespaces)
  2. Avoid using object notation

This will work:

var db = {};    // Put the namespace outside, making it global
db.col = {};    // Put sub-namespaces outside also

(function(){
    db.col.One = 0;    // Avoid using object notation
    db.col.Two = 3;
    db.col.Three = 4;
    db.col.Four = 1;
    db.col.Five = 2;
    db.col.Siz = 7;
    db.col.Seven = 8;

    alert(db.col.Two); alert(db.col.Three); alert(db.col.Four);
})();

One good experiment is:

(function() {
    var db = {};
    db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
    alert(db.col.Two);   // Only one call
    var test = db.col.Three + db.col.Four;   // This statement is side-effect-free
})();

Lo and behold! It works:

alert(3);

However:

(function() {
    var db = {};
    db.col = { One: 0, Two: 3, Three: 4, Four: 1, Five: 2, Six: 7, Seven: 8 };
    alert(db.col.Two);     // First call, anything afterwards is suspect
    alert(db.col.Three);   // Oops!  Cannot eliminate db or db.col!
})();

does NOT work:

var a={a:{f:0,c:3,b:4,e:1,d:2,h:7,g:8}};alert(a.a.c);alert(a.a.b);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文