Kompilasi Lanjutan

Ringkasan

Penggunaan Closure Compiler dengan compilation_level dari ADVANCED_OPTIMIZATIONS menawarkan tingkat kompresi yang lebih baik daripada kompilasi dengan SIMPLE_OPTIMIZATIONS atau WHITESPACE_ONLY. Kompilasi dengan ADVANCED_OPTIMIZATIONS mencapai kompresi tambahan dengan menjadi lebih agresif dalam cara mengubah kode dan mengganti nama simbol. Namun, pendekatan yang lebih agresif ini berarti Anda harus lebih berhati-hati saat menggunakan ADVANCED_OPTIMIZATIONS untuk memastikan kode output berfungsi dengan cara yang sama seperti kode input.

Tutorial ini menggambarkan fungsi tingkat kompilasi ADVANCED_OPTIMIZATIONS dan hal yang dapat Anda lakukan untuk memastikan kode Anda berfungsi setelah kompilasi dengan ADVANCED_OPTIMIZATIONS. Ini juga memperkenalkan konsep extern: simbol yang ditentukan dalam kode di luar kode yang diproses oleh compiler.

Sebelum membaca tutorial ini, Anda harus memahami proses kompilasi JavaScript dengan salah satu alat Closure Compiler (UI layanan compiler, API layanan compiler, atau aplikasi compiler).

Catatan tentang terminologi: flag command line --compilation_level mendukung singkatan ADVANCED dan SIMPLE yang lebih umum digunakan, serta ADVANCED_OPTIMIZATIONS dan SIMPLE_OPTIMIZATIONS yang lebih akurat. Dokumen ini menggunakan bentuk yang lebih panjang, tetapi namanya dapat digunakan secara bergantian di command line.

  1. Kompresi yang Lebih Baik
  2. Cara Mengaktifkan ADVanceD_OPTIMIZATIONS
  3. Yang Perlu Diperhatikan Saat Menggunakan ADVanceD_OPTIMIZATIONS
    1. Penghapusan Kode yang Ingin Anda Pertahankan
    2. Nama Properti Tidak Konsisten
    3. Mengompilasi Dua Bagian Kode Secara Terpisah
    4. Referensi Rusak antara Kode yang Dikompilasi dan Tidak Dikompilasi

Kompresi yang Lebih Baik

Dengan level kompilasi default SIMPLE_OPTIMIZATIONS, Closure Compiler membuat JavaScript lebih kecil dengan mengganti nama variabel lokal. Namun, ada simbol selain variabel lokal yang dapat dipersingkat, dan ada cara untuk menyusutkan kode selain mengganti nama simbol. Kompilasi dengan ADVANCED_OPTIMIZATIONS memanfaatkan berbagai kemungkinan penyingkatan kode.

Bandingkan output SIMPLE_OPTIMIZATIONS dan ADVANCED_OPTIMIZATIONS untuk kode berikut:

function unusedFunction(note) {
  alert(note['text']);
}

function displayNoteTitle(note) {
  alert(note['title']);
}

var flowerNote = {};
flowerNote['title'] = "Flowers";
displayNoteTitle(flowerNote);

Kompilasi dengan SIMPLE_OPTIMIZATIONS mempersingkat kode menjadi:

function unusedFunction(a){alert(a.text)}function displayNoteTitle(a){alert(a.title)}var flowerNote={};flowerNote.title="Flowers";displayNoteTitle(flowerNote);

Kompilasi dengan ADVANCED_OPTIMIZATIONS sepenuhnya mempersingkat kode menjadi:

alert("Flowers");

Kedua skrip ini menghasilkan peringatan yang membaca "Flowers", tetapi skrip kedua jauh lebih kecil.

Level ADVANCED_OPTIMIZATIONS melampaui pemendekan sederhana nama variabel dalam beberapa cara, termasuk:

  • penggantian nama yang lebih agresif:

    Kompilasi dengan SIMPLE_OPTIMIZATIONS hanya mengganti nama parameter note dari fungsi displayNoteTitle() dan unusedFunction(), karena ini adalah satu-satunya variabel dalam skrip yang bersifat lokal untuk fungsi. ADVANCED_OPTIMIZATIONS juga mengganti nama variabel global flowerNote.

  • penghapusan kode mati:

    Kompilasi dengan ADVANCED_OPTIMIZATIONS menghapus fungsi unusedFunction() sepenuhnya, karena tidak pernah dipanggil dalam kode.

  • fungsi inline:

    Kompilasi dengan ADVANCED_OPTIMIZATIONS menggantikan panggilan ke displayNoteTitle() dengan alert() tunggal yang menyusun isi fungsi. Penggantian panggilan fungsi dengan isi fungsi ini dikenal sebagai "sebaris". Jika fungsi lebih panjang atau lebih rumit, inline mungkin dapat mengubah perilaku kode, tetapi Closure Compiler menentukan bahwa dalam hal ini inline inline aman dan menghemat ruang. Kompilasi dengan ADVANCED_OPTIMIZATIONS juga menyisipkan konstanta dan beberapa variabel saat konstanta dapat ditentukan dengan aman.

Daftar ini hanyalah contoh transformasi pengurangan ukuran yang dapat dilakukan oleh kompilasi ADVANCED_OPTIMIZATIONS.

Cara Mengaktifkan ADVanceD_OPTIMIZATIONS

UI layanan Closure Compiler, API layanan, dan aplikasi memiliki metode yang berbeda untuk menetapkan compilation_level ke ADVANCED_OPTIMIZATIONS.

Cara Mengaktifkan ADVanceD_OPTIMIZATIONS di UI layanan Closure Compiler

Guna mengaktifkan ADVANCED_OPTIMIZATIONS untuk UI layanan Closure Compiler, klik tombol pilihan "Advanced".

Cara Mengaktifkan ADVANCED_OPTIMIZATIONS di Closure Compiler service API

Guna mengaktifkan ADVANCED_OPTIMIZATIONS untuk Closure Compiler service API, sertakan parameter permintaan bernama compilation_level dengan nilai ADVANCED_OPTIMIZATIONS, seperti dalam program python berikut:

#!/usr/bin/python2.4

import httplib, urllib, sys

params = urllib.urlencode([
    ('code_url', sys.argv[1]),
    ('compilation_level', 'ADVANCED_OPTIMIZATIONS'),
    ('output_format', 'text'),
    ('output_info', 'compiled_code'),
  ])

headers = { "Content-type": "application/x-www-form-urlencoded" }
conn = httplib.HTTPSConnection('closure-compiler.appspot.com')
conn.request('POST', '/compile', params, headers)
response = conn.getresponse()
data = response.read()
print data
conn.close()

Cara Mengaktifkan ADVANCED_OPTIMIZATIONS di aplikasi Closure Compiler

Guna mengaktifkan ADVANCED_OPTIMIZATIONS untuk aplikasi Closure Compiler, sertakan tanda command line --compilation_level ADVANCED_OPTIMIZATIONS, seperti dalam perintah berikut:

java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js hello.js

Yang Perlu Diperhatikan Saat Menggunakan ADVanceD_OPTIMIZATIONS

Di bawah ini tercantum beberapa efek umum ADVanceD_OPTIMIZATIONS yang tidak diinginkan, dan langkah-langkah yang dapat dilakukan untuk menghindarinya.

Penghapusan Kode yang Ingin Anda Pertahankan

Jika Anda hanya mengompilasi fungsi di bawah ini dengan ADVANCED_OPTIMIZATIONS, Closure Compiler akan menghasilkan output kosong:

function displayNoteTitle(note) {
  alert(note['myTitle']);
}

Karena fungsi tidak pernah dipanggil dalam JavaScript yang Anda teruskan ke compiler, Closure Compiler menganggap kode ini tidak diperlukan.

Dalam banyak kasus, perilaku ini adalah hal yang persis Anda inginkan. Misalnya, jika Anda mengompilasi kode bersama dengan library besar, Closure Compiler dapat menentukan fungsi mana dari library tersebut yang benar-benar Anda gunakan dan menghapus fungsi yang tidak digunakan.

Namun, jika Anda menemukan bahwa Closure Compiler menghapus fungsi yang ingin disimpan, ada dua cara untuk mencegahnya:

  • Pindahkan panggilan fungsi ke dalam kode yang diproses oleh Closure Compiler.
  • Sertakan eksternal untuk fungsi yang ingin Anda ekspos.

Bagian berikutnya akan membahas setiap opsi secara lebih mendetail.

Solusi: Pindahkan Panggilan Fungsi ke Kode yang Diproses oleh Closure Compiler

Anda mungkin mengalami penghapusan kode yang tidak diinginkan jika hanya mengompilasi bagian kode dengan Closure Compiler. Misalnya, Anda mungkin memiliki file library yang hanya berisi definisi fungsi, dan file HTML yang menyertakan library tersebut dan yang berisi kode yang memanggil fungsi tersebut. Dalam hal ini, jika Anda mengompilasi file library dengan ADVANCED_OPTIMIZATIONS, Closure Compiler akan menghapus semua fungsi library Anda.

Solusi paling sederhana untuk masalah ini adalah mengompilasi fungsi Anda secara bersamaan dengan bagian program yang memanggil fungsi tersebut. Misalnya, Closure Compiler tidak akan menghapus displayNoteTitle() saat mengompilasi program berikut:

function displayNoteTitle(note) {
  alert(note['myTitle']);
}
displayNoteTitle({'myTitle': 'Flowers'});

Dalam hal ini, fungsi displayNoteTitle() tidak dihapus karena Closure Compiler melihat fungsi tersebut dipanggil.

Dengan kata lain, Anda dapat mencegah penghapusan kode yang tidak diinginkan dengan menyertakan titik entri program dalam kode yang Anda teruskan ke Closure Compiler. Titik entri program adalah tempat dalam kode tempat program mulai dieksekusi. Misalnya, dalam program catatan bunga dari bagian sebelumnya, tiga baris terakhir dijalankan segera setelah JavaScript dimuat di browser. Ini adalah titik entri untuk program ini. Untuk menentukan kode yang perlu dipertahankan, Closure Compiler dimulai pada titik entri ini dan melacak alur kontrol program ke depan dari sana.

Solusi: Sertakan Eksternal untuk Fungsi yang Ingin Diekspos

Informasi lebih lanjut tentang solusi ini diberikan di bawah dan di halaman eksternal dan ekspor.

Nama Properti Tidak Konsisten

Kompilasi Compiler Closure tidak pernah mengubah literal string dalam kode Anda, apa pun tingkat kompilasi yang Anda gunakan. Artinya, kompilasi dengan ADVANCED_OPTIMIZATIONS memperlakukan properti secara berbeda bergantung pada apakah kode Anda mengaksesnya dengan string. Jika Anda menggabungkan referensi string ke properti dengan referensi titik-sintaksis, Closure Compiler akan mengganti nama beberapa referensi untuk properti tersebut, tetapi tidak untuk yang lain. Akibatnya, kode Anda mungkin tidak akan berjalan dengan benar.

Misalnya, ambil kode berikut:

function displayNoteTitle(note) {
  alert(note['myTitle']);
}
var flowerNote = {};
flowerNote.myTitle = 'Flowers';

alert(flowerNote.myTitle);
displayNoteTitle(flowerNote);

Dua pernyataan terakhir dalam kode sumber ini melakukan hal yang sama persis. Namun, saat mengompresi kode dengan ADVANCED_OPTIMIZATIONS, Anda akan mendapatkan hal ini:

var a={};a.a="Flowers";alert(a.a);alert(a.myTitle);

Pernyataan terakhir dalam kode terkompresi menghasilkan error. Referensi langsung ke properti myTitle telah diganti namanya menjadi a, tetapi referensi yang dikutip menjadi myTitle dalam fungsi displayNoteTitle belum diganti namanya. Akibatnya, pernyataan terakhir mengacu pada properti myTitle yang sudah tidak ada lagi.

Solusi: Konsisten dalam Nama Properti Anda

Solusi ini cukup sederhana. Untuk jenis atau objek tertentu, gunakan string titik sintaksis atau kutipan secara eksklusif. Jangan mencampur sintaksis, terutama dalam referensi ke properti yang sama.

Selain itu, jika memungkinkan, sebaiknya gunakan dot-syntax, karena mendukung pemeriksaan dan pengoptimalan yang lebih baik. Gunakan akses properti string yang dikutip hanya jika Anda tidak ingin Closure Compiler melakukan penggantian nama, seperti saat nama tersebut berasal dari sumber luar, seperti JSON yang didekode.

Mengompilasi Dua Bagian Kode Secara Terpisah

Jika Anda membagi aplikasi menjadi potongan-potongan kode yang berbeda, sebaiknya Anda mengompilasi potongan-potongan itu secara terpisah. Namun, jika dua bagian kode berinteraksi sama sekali, hal ini dapat menyebabkan kesulitan. Meskipun Anda berhasil, output dari dua eksekusi Closure Compiler tidak akan kompatibel.

Misalnya, asumsikan bahwa aplikasi dibagi menjadi dua bagian: bagian yang mengambil data, dan bagian yang menampilkan data.

Berikut adalah kode untuk mengambil data:

function getData() {
  // In an actual project, this data would be retrieved from the server.
  return {title: 'Flower Care', text: 'Flowers need water.'};
}

Berikut adalah kode untuk menampilkan data:

var displayElement = document.getElementById('display');
function displayData(parent, data) {
  var textElement = document.createTextNode(data.text);
  parent.appendChild(textElement);
}
displayData(displayElement, getData());

Jika mencoba mengompilasi dua potongan kode ini secara terpisah, Anda akan mengalami beberapa masalah. Pertama, Closure Compiler menghapus fungsi getData(), karena alasan yang dijelaskan dalam Penghapusan Kode yang Ingin Anda Pertahankan. Kedua, Closure Compiler menghasilkan error fatal saat memproses kode yang menampilkan data.

input:6: ERROR - variable getData is undefined
displayData(displayElement, getData());

Karena compiler tidak memiliki akses ke fungsi getData() saat mengompilasi kode yang menampilkan data, compiler akan memperlakukan getData sebagai belum ditentukan.

Solusi: Kompilasikan Semua Kode untuk Halaman Bersama

Untuk memastikan kompilasi yang tepat, kompilasi semua kode untuk halaman bersama-sama dalam satu proses kompilasi. Closure Compiler dapat menerima beberapa file JavaScript dan string JavaScript sebagai input, sehingga Anda dapat meneruskan kode library dan kode lainnya dalam satu permintaan kompilasi.

Catatan: Pendekatan ini tidak akan berfungsi jika Anda perlu menggabungkan kode yang dikompilasi dan tidak dikompilasi. Lihat Referensi Rusak antara Kode yang Dikompilasi dan Tidak Dikompilasi untuk tips dalam menangani situasi ini.

Referensi Rusak antara Kode yang Dikompilasi dan Tidak Dikompilasi

Penggantian nama simbol di ADVANCED_OPTIMIZATIONS akan menghentikan komunikasi antara kode yang diproses oleh Closure Compiler dan kode lainnya. Kompilasi mengganti nama fungsi yang ditentukan dalam kode sumber Anda. Setiap kode eksternal yang memanggil fungsi Anda akan rusak setelah Anda mengompilasi, karena kode tersebut masih merujuk ke nama fungsi lama. Demikian pula, referensi dalam kode yang dikompilasi ke simbol yang ditentukan secara eksternal dapat diubah oleh Closure Compiler.

Perlu diingat bahwa "kode yang tidak dikompilasi" mencakup kode yang diteruskan ke fungsi eval() sebagai string. Closure Compiler tidak pernah mengubah literal string dalam kode, sehingga Closure Compiler tidak mengubah string yang diteruskan ke pernyataan eval().

Perhatikan bahwa ini adalah masalah yang terkait, tetapi berbeda: mempertahankan komunikasi yang dikompilasi ke eksternal, dan mempertahankan komunikasi eksternal ke kompilasi. Masalah terpisah ini memiliki solusi yang umum, tetapi ada nuansa di setiap sisi. Untuk mendapatkan hasil maksimal dari Closure Compiler, penting untuk memahami kasus mana yang Anda miliki.

Sebelum melanjutkan, sebaiknya Anda memahami eksternal dan ekspor.

Solusi untuk Memanggil Kode Eksternal dari Kode yang Dikompilasi: Kompilasi dengan Eksternal

Jika menggunakan kode yang disediakan ke halaman Anda oleh skrip lain, Anda perlu memastikan bahwa Closure Compiler tidak mengganti nama referensi ke simbol yang ditentukan di library eksternal tersebut. Untuk melakukannya, sertakan file yang berisi eksternal untuk library eksternal ke dalam kompilasi Anda. Tindakan ini akan memberi tahu Closure Compiler mana yang tidak Anda kontrol, sehingga tidak dapat diubah. Kode Anda harus menggunakan nama yang sama dengan yang digunakan file eksternal.

Contoh umumnya adalah API seperti OpenSocial API dan Google Maps API. Misalnya, jika kode Anda memanggil fungsi OpenSocial opensocial.newDataRequest(), tanpa ekstensi yang sesuai, Closure Compiler akan mengubah panggilan ini menjadi a.b().

Solusi untuk Memanggil Kode yang Dikompilasi dari Kode Eksternal: Menerapkan Eksternal

Jika memiliki kode JavaScript yang digunakan kembali sebagai library, Anda mungkin ingin menggunakan Closure Compiler untuk menyusutkan library saja, tetapi tetap mengizinkan kode yang tidak dikompilasi untuk memanggil fungsi dalam library.

Solusi dalam situasi ini adalah menerapkan sekumpulan ekstensi yang menentukan API publik library Anda. Kode Anda akan memberikan definisi untuk simbol yang dideklarasikan dalam ekstensi ini. Artinya, setiap class atau fungsi yang disebutkan oleh ekstensi Anda. Hal ini juga dapat berarti bahwa class Anda mengimplementasikan antarmuka yang dideklarasikan dalam ekstensi.

eksternal ini juga berguna bagi orang lain, tidak hanya untuk Anda sendiri. Konsumen library Anda harus menyertakannya jika mengompilasi kodenya, karena library Anda mewakili skrip eksternal dari perspektif mereka. Anggap ekstensi sebagai kontrak antara Anda dan konsumen, Anda berdua memerlukan salinan.

Untuk itu, pastikan saat mengompilasi kode, Anda juga menyertakan ekstensi dalam kompilasi. Ini mungkin tampak tidak wajar, karena kami sering menganggap ekstern sebagai "berasal dari tempat lain", tetapi perlu untuk memberi tahu Closure Compiler mana simbol yang diekspos, sehingga simbol tersebut tidak diganti namanya.

Satu peringatan penting di sini adalah Anda mungkin mendapatkan diagnostik "definisi duplikat" tentang kode yang menentukan simbol eksternal. Closure Compiler menganggap bahwa semua simbol di bagian eksternal disediakan oleh library luar, dan saat ini tidak dapat memahami bahwa Anda sengaja memberikan definisi. Diagnostik ini aman untuk disembunyikan, dan Anda dapat menganggap penghentian tersebut sebagai konfirmasi bahwa Anda benar-benar memenuhi API.

Selain itu, Closure Compiler dapat memeriksa apakah definisi Anda cocok dengan jenis deklarasi eksternal. Tindakan ini memberikan konfirmasi tambahan bahwa definisi Anda sudah benar.