2

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

関数のカプセル化が高度な変数置換を妨げる理由は何ですか? 両方のスニペットが同じ出力にコンパイルされるようにこれを行う方法はありますか?

4

1 に答える 1

2

考えられる説明の 1 つ:

あなたが言及しているClosure Compilerの機能は「名前空間の平坦化」です。これは、名前空間の長いチェーンでのルックアップに関連するコストを回避しようとするコンパイラの試みです。

たとえばfoo.bar.baz.hello.doSomething();、プロパティを見つけるには、4 つのオブジェクトのチェーンをナビゲートする必要がありdoSomethingます。名前空間のフラット化により、プロパティは にフラット化されa、呼び出しは -- に置き換えられa();ます。これは大幅な改善です。

したがって、2 番目のケースでは、実際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 はすべて 1 回だけ使用されるため、インライン化されます。

var a=0, e=2, f=7, g=8;
alert(3);alert(4);alert(1);

そして最後に、未使用の変数 a、e、f、g が破棄されます。

ただし、これはグローバル スコープでは正常に機能しますが、クロージャー内で定義されたオブジェクトをキャプチャする関数呼び出しがそのクロージャー内にある可能性があるため、コンパイラーはクロージャー内でオブジェクトが定義されている場合は特に注意する必要があります。コンパイラがオブジェクトを「フラット化」してオブジェクトを削除するには、クロージャ内のすべてが「副作用なし」でなければなりません。そうしないと、内部関数呼び出しが参照するキャプチャされたオブジェクトが存在しなくなると、コードが壊れます。

alert()副作用がないとは考えられていません。したがって、 と はへの呼び出しによって変更される可能性があるdbと想定されます。その後の潜在的に副作用のないコードは、変更されたorを参照する可能性があるため、これらのオブジェクトを削除してはなりません。 注:呼び出しが副作用のない最後の呼び出しである場合、これは適用されません。db.colalertdbdb.colalert()

名前空間のフラット化を有効にするには、オブジェクトをクロージャの外に移動し、キャプチャできないグローバル スコープで定義する必要があります。

  1. オブジェクト定義を関数クロージャーの外に移動します (したがって、それらを名前空間にします)。
  2. オブジェクト表記の使用を避ける

これはうまくいきます:

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

1 つの良い実験は次のとおりです。

(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);
于 2011-06-09T04:11:55.497 に答える