Kode JavaScript yang kita tulis pada dasarnya hanyalah teks. Prosesor komputer tidak mengerti teks; prosesor hanya mengerti instruksi mesin yang sangat sederhana. Seseorang harus menerjemahkan teks itu terlebih dahulu — dan pekerjaan itu dipegang oleh JavaScript engine, program yang mem-parse, mengompilasi, dan mengeksekusi kode kita dalam sekejap mata.
Engine menjawab dua kebingungan yang hampir selalu dialami pemula. Pertama, mengapa kode yang sama berjalan di Chrome, Firefox, dan Safari padahal ketiganya memakai engine yang berbeda? Jawabannya: semua engine mematuhi spesifikasi yang sama. Kedua, mengapa bagian kode tertentu berjalan jauh lebih cepat setelah beberapa kali dieksekusi? Jawabannya: optimisasi yang dilakukan engine saat program berjalan (JIT — just-in-time compilation). Chapter ini menyingkap alurnya, lalu membuktikan salah satu konsekuensinya dengan program nyata.
Alur Kerja Engine #
Secara garis besar, setiap engine modern menjalani alur seperti ini:
Mari kita uraikan alur tersebut dalam kata-kata:
- Parser: engine membaca teks kode dan memeriksa tata bahasanya. Kode yang melanggar aturan sintaks ditolak di tahap ini, sebelum program sempat berjalan.
- Abstract Syntax Tree (AST): hasil pembacaan parser berupa struktur pohon yang menggambarkan setiap ekspresi dan statement — versi kode yang jauh lebih mudah diproses mesin daripada teks mentah.
- Interpreter: AST diterjemahkan menjadi bytecode, bentuk instruksi antara yang bisa langsung dieksekusi. Strategi “cepat mulai” ini membuat program berjalan tanpa menunggu kompilasi penuh. Pada V8, interpreter ini bernama Ignition.
- Profiler dan optimizing compiler: engine mengamati bagian kode yang berulang kali dieksekusi (kode panas). Bagian tersebut dikompilasi ulang menjadi kode mesin yang jauh lebih cepat — pada V8, pekerjaan ini dipegang TurboFan. Kompilasi ini bertumpu pada asumsi, misalnya “variabel ini selalu bertipe angka”.
- Deoptimisasi: jika asumsi tadi terbukti keliru di tengah jalan, engine membuang hasil kompilasi dan kembali ke jalur bytecode. Program tetap benar, hanya sedikit lebih lambat.
Selain menerjemahkan kode, engine juga mengelola memori. Ketika kita membuat nilai — sebuah teks, angka, atau struktur data — engine menempatkannya di memori, dan ketika nilai itu sudah tidak bisa dijangkau oleh program, engine membersihkannya lewat proses bernama garbage collection. Kita tidak perlu membebaskan memori secara manual seperti pada beberapa bahasa lain; itulah salah satu alasan JavaScript nyaman untuk pemula. Pembahasan detailnya menunggu kita paham object dan scope pada section berikutnya.
Detail internal berbeda antar engine, tetapi pola besar “mulai cepat, lalu optimalkan bagian yang sibuk” berlaku umum.
Engine yang Populer #
Tiga engine menguasai hampir seluruh dunia JavaScript hari ini:
| Engine | Dipakai oleh |
|---|---|
| V8 | Chrome, Edge, Node.js, Deno |
| SpiderMonkey | Firefox |
| JavaScriptCore | Safari, Bun |
Ketiganya mengimplementasikan spesifikasi ECMA-262 yang sama. Perbedaannya terletak pada kecepatan, penggunaan memori, dan detail internal — bukan pada hasil eksekusi kode yang benar.
Spesifikasi Menjamin Hasil yang Sama #
Klaim “hasilnya sama di mana saja” mudah diucapkan; mari kita buktikan dengan program kecil yang menyentuh cara angka disimpan di dalam JavaScript. Sesuai spesifikasi ECMA-262, tipe Number memakai format IEEE 754 64-bit (double) — dan aturan pembulatannya ditentukan secara detail, sehingga setiap engine yang patuh menghasilkan keluaran identik. Program berikut kami jalankan dengan Node.js 24 (engine V8):
// perilaku-angka.js — hasil identik di V8, SpiderMonkey, dan JavaScriptCore
console.log(0.1 + 0.2);
console.log(2 ** 53);
console.log(2 ** 53 + 1);
console.log(2 ** 53 + 2);Output yang Dihasilkan:
0.30000000000000004
9007199254740992
9007199254740992
9007199254740994Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 2 (
console.log(0.1 + 0.2)): angka0.1dan0.2tidak dapat direpresentasikan secara persis dalam bilangan biner — sama seperti1/3yang tidak habis dalam desimal. Hasil penjumlahan dibulatkan ke float terdekat, menghasilkan0.30000000000000004. Ini bukan cacat engine, melainkan perilaku yang disepakati standar. - Baris 4 (
2 ** 53): operator**berarti pangkat. Nilai2pangkat53adalah9007199254740992— batas di mana seluruh bilangan bulat di bawahnya masih terwakili persis dalam format double. - Baris 5 (
2 ** 53 + 1): pada level bilangan sebesar ini, jarak antar bilangan yang terwakili sudah bernilai2. Penambahan1tidak mungkin direpresentasikan, sehingga hasil dibulatkan kembali ke9007199254740992. Angka1yang kita tambahkan praktis hilang. - Baris 6 (
2 ** 53 + 2): penambahan2pas dengan jarak antar bilangan, sehingga hasilnya tampil benar:9007199254740994.
Jalankan berkas yang sama di Firefox atau Safari dan keluarannya tetap persis seperti di atas. Konsistensi inilah yang membuat kita berani menulis kode satu kali untuk semua browser — jaminannya diberikan oleh standar, bukan oleh kebetulan antar engine.
Pengujian lintas engine juga menjadi kebiasaan penting ketika kita mulai memakai fitur terbaru. Engine yang satu biasanya lebih cepat mengadopsi fitur baru daripada yang lain; standar-lah yang akhirnya menyatukan keduanya, dan bagian standar inilah yang kita bahas pada chapter penutup section ini.
Implikasi bagi Kita sebagai Penulis Kode #
Pemahaman engine tidak berarti kita harus mengutak-atik kode demi kecepatan sejak awal. Justru sebaliknya:
- Engine modern sudah sangat cepat untuk pola kode yang wajar; kejelasan kode lebih berharga daripada trik mikro yang meragukan.
- Optimisasi JIT bekerja paling baik pada kode yang konsisten — variabel yang bertipe tetap dan struktur yang stabil. Kode yang acak-acakan memaksa engine sering deoptimisasi.
- Jika ada bagian program yang benar-benar lambat, ukur dulu dengan profiler (tersedia di DevTools browser maupun Node.js), baru perbaiki bagian yang terbukti menjadi titik lambat.
Dengan memahami engine, istilah bytecode, JIT, dan deoptimisasi tidak lagi terasa asing. Pertanyaan berikutnya: siapa yang memastikan semua engine ini berperilaku seragam? Itulah wilayah kerja standar — topik chapter terakhir pada section ini.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
perilaku-angka.js
Unduh berkas// perilaku-angka.js — hasil identik di V8, SpiderMonkey, dan JavaScriptCore
console.log(0.1 + 0.2);
console.log(2 ** 53);
console.log(2 ** 53 + 1);
console.log(2 ** 53 + 2);