2012-05-03 92 views
2

我最近一直在检查Google Closure编译器。我下载了.jar文件,并给它一个测试驱动器。到目前为止,我必须说我对此印象非常深刻。我当然可以看到它的用处超越最小化。道具给Google团队!Google Closure编译器的ADVANCED_OPTIMIZATIONS选项

虽然我确实有一个小抱怨。在我看来,就优化而言,您只能得到两个选项。它可以是SIMPLE_OPTIMIZATIONS或ADVANCED_OPTIMIZATIONS。前者虽然足够,但是非常简单,恕我直言。首先,除非我遗漏了某些东西,否则它将保留所有的属性名称。它也不会删除无法访问的代码。另一方面,后一种选择太破坏性了。

现在,我相当新的JavaScript,所以很可能我错过了一些东西。如果我说一些愚蠢的话,请随时上学。这就是说,我可以理解JavaScript中重命名的问题。 Google团队建议使用括号表示法(object ['property'])而不是点符号(object.property)来访问您不想更改的属性,并且不要混合这两种用法。他们还建议使用以下模式的“导出”方法:

MyClass = function(name) { 
    this.myName = name; 
}; 

MyClass.prototype.myMethod = function() { 
    alert(this.myName); 
}; 

window['MyClass'] = MyClass; // <-- Constructor 
MyClass.prototype['myMethod'] = MyClass.prototype.myMethod; 

但是,有些合法案例需要混合两种表示法。假设我们正在制作一款游戏。游戏的代码在关闭中完全隔离。它不会将任何东西“出口”到全球范围,也不需要。实际上,它确实不应该碰到窗口对象。不过,它确实需要从XML配置文件读取一些游戏中的属性

示例JavaScript:

var TheGreatAdventure = (function(window) { 

    function Fighter() { 
     // Private to application 
     this.id  = 42; 
     // Accessible to XML configuration system 
     this.name  = 'Generic Jen'; 
     this.hitPoints = 100; 
     this.onAttack = genericFighterAttack; 
     this.onSpeak = genericFighterSpeak; 
     ... 
    } 
    Fighter.publishedProperties = ['name', 'hitPoints', 'onAttack', 'onSpeak'] 

    function genericFighterAttack() {...} 
    function genericFighterSpeak() {...} 

    function cassieAttack() {...} 
    function cassieSpeak() {...} 

    ... 

    EntityReader = { 
     ... 
     function readFromXMLNode(attributes, entityClass, entityInstance) { 
      for (var i = 0; i < attributes.length; i++) { 
       var attribute = attributes[i]; 
       if (attribute.nodeName in entityClass.publishedProperties) 
        entityInstance[attribute.nodeName] = bindContext[attribute.value]; 
      } 
     } 
     ... 
    } 

}(window)); 

示例XML配置文件:

<Fighter name='Custom Cassie' onAttack='cassieAttack' onSpeak='cassieSpeak'/> 

将不仅上述系统不能分配的属性,则所述功能cassieAttack和cassieSpeak会被最小化期间​​作为消除死码!

现在,我无法在整个游戏代码中使用括号表示法访问所有'已发布'属性。即使这样做没有运行时间的损失(应该没有),但仍然有很多额外的输入参与,并且它是(IMO)的眼中钉。有了这些共同的属性,所有东西都会在文本编辑器中显示为一个字符串,从而破坏了语法高亮的目的!

在我看来,一个简单的@preserve(或类似的东西)指令对这些特性将允许ADVANCED_OPTIMIZATIONS与最终方案规模最小的成本来使用。我错过了什么吗?

+0

我有一个类似问题的答案:http://stackoverflow.com/questions/7823811/prevent-google-closure-compiler-from-renaming-settings-objects/7834912#7834912 –

回答

3

这个答案被完全重写,结果有一种方法来做user1127813想要的。

您需要提供一个使用--property_map_input_file标志将某些名称映射到自身的属性映射文件。假设你有以下原代码test.js

/** @constructor */ 
function Fighter() { 
    this.ID  = 42; 
    this.fullName = 'Generic Jen'; 
    this.hitPoints = 100; 
} 
Fighter.publishedProperties = ['fullName', 'hitPoints']; 

var jen = new Fighter(); 
var bob = new Fighter(); 

bob.ID = 54; 
bob.fullName = 'Bob the Destructor'; 
bob.hitPoints = 1337; 

for(i = 0; i < Fighter.publishedProperties.length; i++) { 
    prop = Fighter.publishedProperties[i]; 
    alert(prop + ' = ' + bob[prop]); 
} 

编译它像这样:

java -jar closure-compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js test.js --property_map_output_file testprop.txt --js_output_file test2.js 

你会得到一个新的文件test2.js(与不工作的内容),另一个文件testprop.txt那包含:

ID:a 
hitPoints:c 
fullName:b 

变化testprop.txt所以它看起来是这样的:

ID:ID 
hitPoints:hitPoints 
fullName:fullName 

然后用testprop.txt重新编译作为输入,而不是输出:

java -jar closure-compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js test.js --property_map_input_file testprop.txt --js_output_file test2.js 

观察的test2.js内容:

var a=["fullName","hitPoints"],b=new function(){};b.ID=54;b.fullName="Bob the Destructor";b.hitPoints=1337;for(i=0;i<a.length;i++)prop=a[i],alert(prop+" = "+b[prop]); 

所需的性质与它们的原始名称使用点符号现在访问并且该程序将正确地显示带有已发布的bob属性的弹出窗口。

+0

首先,谢谢。但是,难道你不认为简单地指示编译器的重命名模块留下一小部分标识符,而不是在整个程序中使用括号表示法(所有40K行)?另外,当/如果你决定使用另一个极小值时会发生什么?你坚持使用括号内的字符串。这是为了引起副作用(即让编译器仅留下某些变量)而改变不应该的东西(程序代码)的情况。 – user1127813

+0

至于使用属性映射,恐怕它是不可行的,因为在上面的例子中,XML文件将由应用程序的用户提供(我想我没有说清楚)。 – user1127813

+0

你是对的功能。它们当然应该是一个对象的方法(由readFromXMLNode()中的bindContext变量表示)。 – user1127813

3

编译器支持这个功能,但它是尴尬,并依赖于一个“原始”从Closure库被称为“goog.reflect.object”:

/** @nocollapse */ 
Fighter.publishedProperties = goog.object.transpose(goog.reflect.object(
    Fighter, {fullName:1, hitPoints:2})); 

这避免使用引用属性。如果这是您从Closure库中使用的唯一东西,但“goog.object.transpose”函数将被编译出来(goog.object.transpose并不特别,因此您可以自由使用替代实现)。这是类型安全的,可以与编译器基于类型的优化一起使用(请参阅此处的描述:http://code.google.com/p/closure-compiler/wiki/ExperimentalTypeBasedPropertyRenaming)。

关键是,必须直接使用goog.reflect.object,并将构造函数和对象文本与需要保留的属性的键一起使用。

另一件事,你会想知道这一点,在ADVANCED模式下,如果Fighter.publishedProperties定义在全局范围内,由于名称空间崩溃,它将从构造函数中删除。 @nocollapse可以防止这种情况发生。另一种方法是使用一个辅助的方法来添加属性:

function addPublishedProperties(obj, value) { 
    obj.publishedProperties = goog.object.transpose(value); 
} 

好吧,我在这里讨论了很多理由,所以一定要让我知道,如果你想澄清。

相关问题