一种可能的解释:
您所指的 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.col
可以通过调用 来修改alert
。之后任何可能没有副作用的代码都可以引用修改后的db
or db.col
,因此不能消除这些对象。 注意:如果呼叫是最后一个非无副作用的呼叫,则这不适用。alert()
要启用命名空间展平,您必须将对象移到闭包之外并在全局范围内定义它们,这是无法捕获的:
- 将对象定义移到函数闭包之外(因此使它们成为命名空间)
- 避免使用对象符号
这将起作用:
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);