闭包编译器ADVANCED_OPTIMIZATIONS和函数封装

时间:2011-06-08 21:35:21

标签: javascript google-closure-compiler

使用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);

函数封装阻止高级变量替换的原因是什么?有没有办法这样做,以便两个片段编译到相同的输出?

1 个答案:

答案 0 :(得分:2)

一种可能的解释:

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

例如,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()不被认为是无副作用的。因此,假设可以通过调用db来修改db.colalert。之后可能没有副作用的任何代码都可以引用修改后的dbdb.col,因此不得删除这些对象。 注意:如果alert()来电是非副作用的最后一次通话,则不适用。

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

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

    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);