2012-05-03 118 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); 
} 

好吧,我在這裏討論了很多理由,所以一定要讓我知道,如果你想澄清。

相關問題