Kapatma Derleyici Tarafından Uygulanan Kısıtlamaları Anlama

Closure Compiler, JavaScript girişinin birkaç kısıtlamaya uymasını bekler. Derleyici'den gerçekleştirmesini istediğiniz optimizasyon düzeyi ne kadar yüksek olursa Derleyici'nin giriş JavaScript'ine o kadar fazla kısıtlaması olur.

Bu dokümanda, her optimizasyon düzeyi için temel kısıtlamalar açıklanmaktadır. Derleyicinin varsayımları için bu wiki sayfasına bakın.

Tüm optimizasyon seviyeleri için kısıtlamalar

Derleyici, tüm optimizasyon seviyeleri için işlediği tüm JavaScript'e aşağıdaki iki kısıtlamayı yerleştirir:

  • Derleyen yalnızca ECMAScript'i tanır.

    ECMAScript 5, hemen her yerde desteklenen JavaScript sürümüdür. Ancak derleyici, ECMAScript 6'daki birçok özelliği de desteklemektedir. Derleyici yalnızca resmi dil özelliklerini destekler.

    Uygun ECMAScript dil spesifikasyonuna uyan tarayıcıya özel özellikler, derleyici ile sorunsuz bir şekilde çalışır. Örneğin, ActiveX nesneleri yasal JavaScript söz dizimiyle oluşturulur. Bu nedenle, ActiveX nesneleri oluşturan kod derleyici ile çalışır.

    Derleyici bakım görevlileri, yeni dil sürümlerini ve özelliklerini desteklemek için aktif şekilde çalışır. Projeler, --language_in işaretini kullanarak hangi ECMAScript dil sürümünü istediklerini belirtebilir.

  • Derleyen, yorumları korumaz.

    Tüm Derleyici optimizasyon seviyeleri yorumları kaldırır. Bu nedenle, özel olarak biçimlendirilmiş yorumlara bağlı olan kodlar Derleyici ile çalışmaz.

    Örneğin, Derleyici yorumları saklamadığı için, JScript'in "koşullu yorumlarını" doğrudan kullanamazsınız. Bununla birlikte, koşullu yorumları eval() ifadeleri içinde sarmalayarak bu kısıtlamayı aşabilirsiniz. Derleyen, herhangi bir hata oluşturmadan aşağıdaki kodu işleyebilir:

     x = eval("/*@cc_on 2+@*/ 0");
    

    Not: Açık kaynak lisansları ve diğer önemli metinleri, @preserve ek açıklamasını kullanarak Derleyici çıkışının en üstüne ekleyebilirsiniz.

SIMPLE_OPTIMIZATIONS için kısıtlamalar

Basit optimizasyon düzeyi, kod boyutunu azaltmak için işlev parametrelerini, yerel değişkenleri ve yerel olarak tanımlanan işlevleri yeniden adlandırır. Ancak, bazı JavaScript yapıları bu yeniden adlandırma sürecini bozabilir.

SIMPLE_OPTIMIZATIONS kullanırken aşağıdaki yapılardan ve uygulamalardan kaçının:

  • with:

    Derleyici, with kullandığınızda yerel bir değişken ile aynı ada sahip bir nesne özelliğini ayırt edemediği için adın tüm örneklerini yeniden adlandırır.

    Ayrıca with ifadesi, kodunuzu insanların okumasını zorlaştırır. with ifadesi, ad çözümleme için normal kuralları değiştirir ve kodu yazan programcının bir adı ne ifade ettiğini belirlemeyi daha da zorlaştırabilir.

  • eval():

    Derleyici, eval() dizesinin bağımsız değişkenini ayrıştırmaz. Bu nedenle, bu bağımsız değişken içindeki hiçbir sembolü yeniden adlandırmaz.

  • İşlev veya parametre adlarının dize temsilleri:

    Derleyici, işlevleri ve işlev parametrelerini yeniden adlandırır. Ancak kodunuzdaki işlevlere veya parametrelere atıfta bulunan hiçbir dizeyi değiştirmez. Bu nedenle, işlev veya parametre adlarını kodunuzda dize olarak temsil etmekten kaçınmalısınız. Örneğin, argumentNames() Prototip kitaplığı işlevi bir işlevin parametrelerinin adlarını almak için Function.toString() kullanır. Ancak argumentNames(), kodunuzdaki bağımsız değişken adlarını kullanmaya teşvik edebilir ancak Basit mod derlemesi bu tür bir referansı bozar.

ADVANCED_OPTIMIZATIONS için kısıtlamalar

ADVANCED_OPTIMIZATIONS derleme düzeyi, SIMPLE_OPTIMIZATIONS ile aynı dönüşümleri gerçekleştirir ve ayrıca özelliklerin, değişkenlerin ve işlevlerin yeniden adlandırılmasını, geçersiz kodların ortadan kaldırılmasını ve mülkün düzleştirilmesini de sağlar. Bu yeni kartlar, giriş JavaScript'ine ek kısıtlamalar uyguluyor. Genel olarak, JavaScript'in dinamik özelliklerini kullanmak, kodunuzda doğru statik analizi engeller.

Genel değişken, işlev ve mülkün yeniden adlandırılmasının etkileri:

ADVANCED_OPTIMIZATIONS alanının genel olarak yeniden adlandırılması aşağıdaki uygulamaları tehlikeli kılıyor:

  • Beyan edilmemiş harici referanslar:

    Derleyicinin, değişkenleri, işlevleri ve özellikleri doğru şekilde yeniden adlandırmak için Derleyici'nin bu global referansların tümü hakkında bilgi sahibi olması gerekir. Derleyen'e, derlenen kodun dışında tanımlanan simgeler hakkında bilgi vermelisiniz. Gelişmiş Derleme ve İstisnalar, harici simgelerin nasıl belirtileceğini açıklar.

  • Harici kodda dışa aktarılmayan dahili adları kullanma:

    Derlenen kod, derlenmemiş kodla ilgili tüm simgeleri dışa aktarmalıdır. Gelişmiş Derleme ve İstisnalar, simgelerin nasıl dışa aktarılacağını açıklar.

  • Nesne özelliklerini belirtmek için dize adlarını kullanma:

    Derleyici, Gelişmiş modda özellikleri yeniden adlandırır ancak dizeleri hiçbir zaman yeniden adlandırmaz.

      var x = { renamed_property: 1 };
      var y = x.renamed_property; // This is OK.
    
      // 'renamed_property' below doesn't exist on x after renaming, so the
      //  following evaluates to false.
      if ( 'renamed_property' in x ) {}; // BAD
    
      // The following also fails:
      x['renamed_property']; // BAD
    

    Tırnak içine alınmış dize içeren bir mülke referans vermeniz gerekiyorsa her zaman tırnak içine alınmış bir dize kullanın:

      var x = { 'unrenamed_property': 1 };
      x['unrenamed_property'];  // This is OK.
      if ( 'unrenamed_property' in x ) {};   // This is OK
    
  • Değişkenleri genel nesnenin özellikleri olarak tanımlama:

    Derleyici, özellikleri ve değişkenleri bağımsız olarak yeniden adlandırır. Örneğin Derleyici, aşağıdaki iki referansı aynı şekilde foo olsa da farklı şekilde ele alır:

      var foo = {};
      window.foo; // BAD
    

    Bu kod şu konuma derlenebilir:

      var a = {};
      window.b;
    

    Bir değişkeni, genel nesnenin özelliği olarak belirtmeniz gerekirse her zaman şu şekilde başvuruda bulunun:

    window.foo = {}
    window.foo;
    

Implications of dead code elimination

The ADVANCED_OPTIMIZATIONS compilation level removes code that is never executed. This elimination of dead code makes the following practices dangerous:

  • Calling functions from outside of compiled code:

    When you compile functions without compiling the code that calls those functions, the Compiler assumes that the functions are never called and removes them. To avoid unwanted code removal, either:

    • compile all the JavaScript for your application together, or
    • export compiled functions.

    Advanced Compilation and Externs describes both of these approaches in greater detail.

  • Retrieving functions through iteration over constructor or prototype properties:

    To determine whether a function is dead code, the Compiler has to find all the calls to that function. By iterating over the properties of a constructor or its prototype you can find and call methods, but the Compiler can't identify the specific functions called in this manner.

    For example, the following code causes unintended code removal:

    function Coordinate() {
    }
    Coordinate.prototype.initX = function() {
      this.x = 0;
    }
    Coordinate.prototype.initY = function() {
      this.y = 0;
    }
    var coord = new Coordinate();
    for (method in Coordinate.prototype) {
      Coordinate.prototype[method].call(coord); // BAD
    }
        

    Derleyici, initX() ve initY() öğelerinin for döngüsünde çağrıldığını anlayamadığı için bu yöntemlerin ikisini de kaldırır.

    Bir işlevi parametre olarak geçirirseniz Derleyici'nin bu parametreye yapılan çağrıları bulabileceğini unutmayın. Örneğin Derleyici, aşağıdaki kodu Gelişmiş modda derlerken getHello() işlevini kaldırmaz.

    function alertF(f) {
      alert(f());
    }
    function getHello() {
      return 'hello';
    }
    // The Compiler figures out that this call to alertF also calls getHello().
    alertF(getHello); // This is OK.
        

Nesne özelliği birleştirmenin etkileri

Derleyici, Gelişmiş modda nesne özelliklerini daraltarak ad kısaltmaya hazır olur. Örneğin, Derleyici bunu dönüştürür:

   var foo = {};
   foo.bar = function (a) { alert(a) };
   foo.bar("hello");

bunun için:

   var foo$bar = function (a) { alert(a) };
   foo$bar("hello");

Bu mülkün daha sonra yeniden adlandırılması, daha sonra yeniden adlandırma kartına daha verimli bir şekilde yeniden ad verme olanağı sağlar. Derleyici, örneğin foo$bar ifadesini tek bir karakterle değiştirebilir.

Ancak mülk birleştirme aşağıdaki uygulamaları da tehlikeli hale getirir:

  • Oluşturucuların ve prototip yöntemlerinin dışında this kullanma:

    Özellik düzleştirme, bir işlev içindeki this anahtar kelimesinin anlamını değiştirebilir. Örneğin:

       var foo = {};
       foo.bar = function (a) { this.bad = a; }; // BAD
       foo.bar("hello");
    

    şu hale gelir:

       var foo$bar = function (a) { this.bad = a; };
       foo$bar("hello");
    

    Dönüşümden önceki this, foo.bar içinde foo anlamına gelir. Dönüşümden sonra this, global this anlamına gelir. Derleyici bu gibi durumlarda şu uyarıyı oluşturur:

    "WARNING - dangerous use of this in static method foo.bar"

    Mülk birleştirmenin this referanslarınıza zarar vermesini önlemek için yalnızca oluşturucuların ve prototip yöntemlerinin içinde this kullanın. this anahtar kelimesinin anlamı, new anahtar kelimesiyle bir oluşturucuyu veya prototype özelliği olan bir işlevin içindeyken belirtilir.

  • Statik yöntemleri, hangi sınıfta çağrıldıklarını bilmeden kullanmak:

    Örneğin, aşağıdaki yöntemlerden birini kullanıyorsanız:

    class A { static create() { return new A(); }};
    class B { static create() { return new B(); }};
    let cls = someCondition ? A : B;
    cls.create();
    
    derleyici, create yöntemini (ES6'dan ES5'e dönüşümden sonra) daraltır. Bu nedenle cls.create() çağrısı başarısız olur. Bu durumu @nocollapse ek açıklamasıyla önleyebilirsiniz:
    class A {
      /** @nocollapse */
      static create() {
        return new A();
      }
    }
    class B {
      /** @nocollapse */
      static create() {
        return new A();
      }
    }
    

  • Süper sınıfı bilmeden statik bir yöntemde süper kullanma:

    Derleyici, super.sayHi() özelliğinin Parent.sayHi() anlamına geldiğini bildiği için aşağıdaki kod güvenlidir:

    class Parent {
      static sayHi() {
        alert('Parent says hi');
      }
    }
    class Child extends Parent {
      static sayHi() {
        super.sayHi();
      }
    }
    Child.sayHi();
    

    Ancak mülk birleştirme, myMixin(Parent).sayHi derlenmemiş Parent.sayHi değerine eşit olsa bile aşağıdaki kodu bozar:

    class Parent {
      static sayHi() {
        alert('Parent says hi');
      }
    }
    class Child extends myMixin(Parent) {
      static sayHi() {
        super.sayHi();
      }
    }
    Child.sayHi();
    

    /** @nocollapse */ ek açıklamasıyla bu bölünmeyi önleyin.

  • Object.defineProperties veya ES6 getter/setter öğelerini kullanma:

    Derleyici bu yapıları iyi anlamıyor. ES6 alıcı ve setleri, harf çevirisi aracılığıyla Object.defineProperties(...) biçimine dönüştürülür. Şu anda derleyici bu yapıyı statik olarak analiz edememektedir ve özelliklere erişimin ve setlerin yan etkisi olmadığını varsayar. Bu durum tehlikeli sonuçlara yol açabilir. Örneğin:

    class C {
      static get someProperty() {
        console.log("hello getters!");
      }
    }
    var unused = C.someProperty;
    

    Derlenir:

    C = function() {};
    Object.defineProperties(C, {a: // Note someProperty is also renamed to 'a'.
      {configurable:!0, enumerable:!0, get:function() {
        console.log("hello world");
        return 1;
    }}});
    

    C.someProperty'in yan etkisi olmadığı tespit edildiğinden kaldırıldı.