Closure Compiler mengharapkan input JavaScript-nya mematuhi beberapa batasan. Semakin tinggi tingkat pengoptimalan yang harus dilakukan Compiler, semakin banyak batasan yang ditetapkan Compiler pada JavaScript input.
Dokumen ini menjelaskan batasan utama untuk setiap tingkat pengoptimalan. Lihat juga halaman wiki ini untuk asumsi tambahan yang dibuat oleh compiler.
Pembatasan untuk semua tingkat pengoptimalan
Compiler menempatkan dua pembatasan berikut pada semua JavaScript yang diprosesnya, untuk semua tingkat pengoptimalan:
Compiler hanya mengenali ECMAScript.
ECMAScript 5 adalah versi JavaScript yang didukung hampir di mana saja. Namun, compiler juga mendukung banyak fitur di ECMAScript 6. Compiler hanya mendukung fitur bahasa resmi.
Fitur khusus browser yang sesuai dengan spesifikasi bahasa ECMAScript yang sesuai akan berfungsi baik dengan compiler. Misalnya, objek ActiveX dibuat dengan sintaksis JavaScript legal, sehingga kode yang membuat objek ActiveX berfungsi dengan compiler.
Pengelola compiler secara aktif bekerja untuk mendukung versi bahasa baru dan fiturnya. Project dapat menentukan versi bahasa ECMAScript yang diinginkan menggunakan flag
--language_in
.Compiler tidak menyimpan komentar.
Semua level pengoptimalan Compiler menghapus komentar, sehingga kode yang mengandalkan komentar yang diformat secara khusus tidak berfungsi dengan Compiler.
Misalnya, karena Compiler tidak mempertahankan komentar, Anda tidak dapat menggunakan "komentar bersyarat" JScript secara langsung. Namun, Anda dapat mengatasi pembatasan ini dengan menggabungkan komentar bersyarat dalam ekspresi
eval()
. Compiler dapat memproses kode berikut tanpa menghasilkan error:x = eval("/*@cc_on 2+@*/ 0");
Catatan: Anda dapat menyertakan lisensi open source dan teks penting lainnya di bagian atas output Compiler menggunakan anotasi @preserve.
Pembatasan untuk SIMPLE_OPTIMIZATIONS
Tingkat Pengoptimalan sederhana mengganti nama parameter fungsi, variabel lokal, dan fungsi yang ditentukan secara lokal untuk mengurangi ukuran kode. Namun, beberapa konstruksi JavaScript dapat merusak proses penggantian nama ini.
Hindari
konstruksi dan praktik berikut saat
menggunakan SIMPLE_OPTIMIZATIONS
:
with
:Saat Anda menggunakan
with
, Compiler tidak dapat membedakan antara variabel lokal dan properti objek dengan nama yang sama, sehingga dapat mengganti nama semua instance nama.Selain itu, pernyataan
with
membuat kode Anda lebih sulit dibaca manusia. Pernyataanwith
mengubah aturan normal untuk resolusi nama, dan dapat menyulitkan bahkan bagi programmer yang menulis kode untuk mengidentifikasi arti nama.eval()
:Compiler tidak mengurai argumen string
eval()
, sehingga tidak akan mengganti nama simbol apa pun dalam argumen ini.Representasi string dari fungsi atau nama parameter:
Compiler mengganti nama fungsi dan parameter fungsi, tetapi tidak mengubah string apa pun dalam kode Anda yang merujuk ke fungsi atau parameter berdasarkan nama. Oleh karena itu, hindari nama fungsi atau parameter sebagai string dalam kode Anda. Misalnya, fungsi library Prototype
argumentNames()
menggunakanFunction.toString()
untuk mengambil nama parameter fungsi. Namun, meskipunargumentNames()
mungkin mendorong Anda untuk menggunakan nama argumen dalam kode, kompilasi Mode sederhana akan merusak referensi semacam ini.
Pembatasan untuk ADVanceD_OPTIMIZATIONS
Tingkat kompilasi ADVANCED_OPTIMIZATIONS
menjalankan
transformasi yang sama seperti SIMPLE_OPTIMIZATIONS
, dan juga menambahkan penggantian nama global untuk properti, variabel, dan fungsi,
penghapusan kode mati, dan perataan properti. Proses baru ini menerapkan
batasan tambahan pada JavaScript input. Secara umum, penggunaan
fitur dinamis JavaScript akan mencegah analisis statis yang benar
pada kode Anda.
Implikasi penggantian variabel, fungsi, dan properti global:
Penggantian nama global ADVANCED_OPTIMIZATIONS
membuat
praktik berikut berbahaya:
Referensi eksternal yang tidak dideklarasikan:
Untuk mengganti nama variabel, fungsi, dan properti global dengan benar, Compiler harus mengetahui semua referensi ke global tersebut. Anda harus memberi tahu Compiler tentang simbol yang ditentukan di luar kode yang sedang dikompilasi. Kompilasi Lanjutan dan Externs menjelaskan cara mendeklarasikan simbol eksternal.
Menggunakan nama internal yang tidak diekspor dalam kode eksternal:
Kode yang dikompilasi harus mengekspor simbol apa pun yang dirujuk oleh kode yang tidak dikompilasi. Advanced Kompilasiation and Externs menjelaskan cara mengekspor simbol.
Menggunakan nama string untuk merujuk ke properti objek:
Compiler mengganti nama properti dalam mode Lanjutan, tetapi tidak pernah mengganti nama string.
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
Jika Anda perlu merujuk ke properti dengan string yang dikutip, selalu gunakan string yang dikutip:
var x = { 'unrenamed_property': 1 }; x['unrenamed_property']; // This is OK. if ( 'unrenamed_property' in x ) {}; // This is OK
Merujuk ke variabel sebagai properti objek global:
Compiler mengganti nama properti dan variabel secara independen. Misalnya, Compiler memperlakukan dua referensi berikut untuk
foo
secara berbeda, meskipun keduanya setara:var foo = {}; window.foo; // BAD
Kode ini mungkin dikompilasi menjadi:
var a = {}; window.b;
Jika Anda perlu merujuk ke variabel sebagai properti objek global, selalu lihat variabel tersebut:
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 }
Compiler tidak memahami bahwa
initX()
daninitY()
dipanggil dalam loopfor
, sehingga akan menghapus kedua metode ini.Perhatikan bahwa jika Anda meneruskan fungsi sebagai parameter, Compiler dapat menemukan panggilan ke parameter tersebut. Misalnya, Compiler tidak menghapus fungsi
getHello()
saat mengompilasi kode berikut dalam mode Lanjutan.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.
Implikasi perataan properti objek
Dalam mode Lanjutan, Compiler menciutkan properti objek untuk mempersiapkan penyingkatan nama. Misalnya, Compiler mengubah ini:
var foo = {}; foo.bar = function (a) { alert(a) }; foo.bar("hello");
menjadi ini:
var foo$bar = function (a) { alert(a) }; foo$bar("hello");
Dengan meratakan properti ini, penggantian nama kartu di masa mendatang dapat dilakukan dengan lebih efisien. Misalnya, Compiler dapat mengganti foo$bar
dengan
satu karakter.
Namun, meratakan properti juga membuat praktik berikut berbahaya:
Menggunakan
this
di luar konstruktor dan metode prototipe:Perataan properti dapat mengubah arti kata kunci
this
dalam suatu fungsi. Contoh:var foo = {}; foo.bar = function (a) { this.bad = a; }; // BAD foo.bar("hello");
menjadi:
var foo$bar = function (a) { this.bad = a; }; foo$bar("hello");
Sebelum transformasi,
this
dalamfoo.bar
mengacu padafoo
. Setelah transformasi,this
merujuk kethis
global. Dalam kasus seperti ini, Compiler menghasilkan peringatan ini:"WARNING - dangerous use of this in static method foo.bar"
Agar properti tidak merata agar tidak merusak referensi Anda ke
this
, hanya gunakanthis
dalam konstruktor dan metode prototipe. Artithis
tidak ambigu saat Anda memanggil konstruktor dengan kata kuncinew
, atau dalam fungsi yang merupakan properti dariprototype
.Menggunakan metode statis tanpa mengetahui class mana yang memanggilnya:
Misalnya, jika Anda memiliki:
class A { static create() { return new A(); }}; class B { static create() { return new B(); }}; let cls = someCondition ? A : B; cls.create();
compiler akan menciutkan kedua metodecreate
(setelah melakukan transpilasi dari ES6 ke ES5) sehingga panggilancls.create()
akan gagal. Anda dapat menghindari hal ini dengan anotasi@nocollapse
:class A { /** @nocollapse */ static create() { return new A(); } } class B { /** @nocollapse */ static create() { return new A(); } }
Menggunakan super dalam metode statis tanpa mengetahui superclass:
Kode berikut aman karena compiler tahu bahwa
super.sayHi()
merujuk keParent.sayHi()
:class Parent { static sayHi() { alert('Parent says hi'); } } class Child extends Parent { static sayHi() { super.sayHi(); } } Child.sayHi();
Namun, meratakan properti akan merusak kode berikut, meskipun
myMixin(Parent).sayHi
sama denganParent.sayHi
yang tidak dikompilasi:class Parent { static sayHi() { alert('Parent says hi'); } } class Child extends myMixin(Parent) { static sayHi() { super.sayHi(); } } Child.sayHi();
Hindari kerusakan ini dengan anotasi
/** @nocollapse */
.Menggunakan pengambil/penyetel Object.defineProperties atau ES6:
Compiler tidak memahami konstruksi ini dengan baik. Pengambil dan penyetel ES6 diubah menjadi Object.defineProperties(...) melalui transpilasi. Saat ini, compiler tidak dapat menganalisis konstruksi ini secara statis dan mengasumsikan akses serta penetapan properti bebas efek samping. Ini dapat menimbulkan dampak yang berbahaya. Contoh:
class C { static get someProperty() { console.log("hello getters!"); } } var unused = C.someProperty;
Dikompilasi menjadi:
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 dianggap tidak memiliki efek samping sehingga dihapus.