2

最近、Google Closure Compiler を調べています。.jar ファイルをダウンロードして、テストドライブを提供しました。これまでのところ、私は非常に感銘を受けたと言わざるを得ません。最小化を超えたその有用性は確かにわかります。Google チームへの小道具!

ただし、小さな不満が 1 つあります。最適化に関する限り、2 つのオプションしか得られないように思えます。SIMPLE_OPTIMIZATIONS または ADVANCED_OPTIMIZATIONS のいずれかです。前者は十分ですが、非常に単純です。1 つには、何かが欠けていない限り、すべてのプロパティ名は変更されません。また、到達不能コードは削除されません。一方、後者のオプションは破壊的すぎます。

現在、私は JavaScript にかなり慣れていないため、何かが欠けている可能性が非常に高いです。私がばかげたことを言ったら、遠慮なく私に教えてください。そうは言っても、JavaScript での名前変更の問題は理解できます。Google チームは、ドット表記 (object.property) の代わりにブラケット表記 (object['property']) を使用して、変更したくないプロパティにアクセスし、2 つの使用方法を混在させないことをお勧めします。また、次のパターンを使用して「エクスポート」する方法も提案しています。

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

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

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

ただし、2 つの表記法を混在させたい正当なケースもあります。ゲームを構築しているとしましょう。ゲームのコードは、クロージャ内で完全に分離されています。グローバルスコープには何も「エクスポート」しませんし、その必要もありません。実際、window オブジェクトに触れるべきではありません。ただし、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 を最終的なプログラム サイズの最小コストで使用できるようになります。何か不足していますか?

4

2 に答える 2

3

コンパイラはこれをサポートしていますが、扱いにくく、"goog.reflect.object" と呼ばれる Closure Library の "プリミティブ" に依存しています。

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

これにより、引用されたプロパティの使用が回避されます。これが Closure Library から使用する唯一のものである場合、「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);
}

わかりました、ここで多くのことを説明しましたので、説明が必要な場合はお知らせください。

于 2012-05-03T19:02:51.127 に答える
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 の公開されたプロパティを含むポップアップを正しく表示します。

于 2012-05-03T14:46:51.420 に答える