Setiap kali kita menjalankan node app.mjs, ada satu komponen yang membaca, menerjemahkan, dan mengeksekusi kode JavaScript kita: V8. Mesin ini dipilih Ryan Dahl pada 2009 dan sampai sekarang tetap menjadi jantung Node.js. Memahami peran V8 membantu kita menjawab pertanyaan praktis: dari mana Node.js mendukung fitur JavaScript terbaru, mengapa pola kode tertentu berjalan lebih cepat, dan apa sebenarnya yang Node.js tambahkan di atas mesin tersebut. Chapter ini membedah peran V8 dan alur kerjanya secara bertahap.
V8, Mesin di Jantung Node.js #
V8 adalah mesin JavaScript (JavaScript engine) open-source buatan Google yang ditulis dalam bahasa C++. Mesin yang sama mengeksekusi JavaScript di browser Chrome, sehingga kode JavaScript yang kita tulis untuk Node.js dievaluasi oleh teknologi identik dengan yang dipakai ratusan juta pengguna Chrome. Tiga tanggung jawab utama V8: mengompilasi kode JavaScript menjadi instruksi yang bisa dijalankan prosesor, mengeksekusi instruksi tersebut, dan mengelola memori melalui mekanisme garbage collection yang otomatis membersihkan object yang tidak lagi terpakai.
Meski sentral, V8 tidak bekerja sendirian. Node.js menyusun empat lapisan komponen berikut:
| Komponen | Peran |
|---|---|
| V8 | Menjalankan JavaScript: parsing, kompilasi, eksekusi, dan garbage collection |
| libuv | Event loop, I/O asynchronous, dan thread pool untuk berkas, jaringan, serta timer |
| Bindings C++ | Jembatan yang menghubungkan fungsi V8 dengan kemampuan sistem operasi |
| Core modules | API siap pakai seperti node:fs, node:http, dan node:path |
V8 sama sekali tidak tahu cara membaca berkas atau menerima koneksi jaringan — hal tersebut diurus oleh libuv dan bindings, lalu dibungkus menjadi core modules yang bisa kita panggil dari JavaScript. Inilah sebabnya document dan API DOM tidak ada di Node.js: keduanya adalah kemampuan yang disediakan browser, bukan V8.
Dari Kode Sumber ke Instruksi Mesin #
V8 tidak menerjemahkan JavaScript baris demi baris seperti halnya membaca naskah. Mesin ini menempuh alur bertingkat agar waktu mulai program tetap singkat, namun performa jangka panjangnya tetap tinggi:
- Parsing — V8 membaca teks sumber, memecahnya menjadi token, lalu menyusun abstract syntax tree (AST), representasi berbentuk pohon dari struktur kode kita. Kesalahan sintaks terdeteksi pada tahap ini.
- Kompilasi bytecode — AST diterjemahkan menjadi bytecode ringkas oleh interpreter Ignition, lalu bytecode langsung dieksekusi. Tahap ini membuat waktu mulai program tetap pendek.
- Pengumpulan umpan balik — selama bytecode berjalan, V8 mencatat pola nyata: tipe data yang lewat, bentuk object yang dipakai, dan cabang kode yang sering dieksekusi.
- Optimasi (JIT) — fungsi yang sangat sering dieksekusi (“panas”) dikompilasi ulang menjadi kode mesin asli oleh lapisan optimasi seperti Sparkplug, Maglev, dan TurboFan, berdasarkan umpan balik yang terkumpul. Teknik kompilasi saat program berjalan inilah yang disebut just-in-time compilation (JIT).
- Deoptimisasi — bila asumsi optimasi ternyata meleset (misalnya tipe data berubah), V8 membuang versi teroptimasi dan kembali ke bytecode. Proses bolak-balik ini normal, tetapi pola kode yang tidak konsisten membuatnya sering terjadi.
Melihat V8 Bekerja #
Kode berikut memakai fitur JavaScript modern — class, arrow function, dan template literal — lalu mencetak versi V8 yang mengeksekusinya:
class Bilangan {
constructor(nilai) {
this.nilai = nilai;
}
ganda() {
return this.nilai * 2;
}
}
const hasil = [1, 2, 3, 4, 5].map((n) => new Bilangan(n).ganda());
console.log(`Hasil map: ${hasil.join(", ")}`);
console.log(`Dieksekusi oleh V8 versi ${process.versions.v8}`);Output yang Dihasilkan:
Hasil map: 2, 4, 6, 8, 10
Dieksekusi oleh V8 versi 13.6.233.17-node.53Mari kita telaah baris-baris kode di atas:
- Baris 1-9 (
class Bilangan): definisi class dengan satu propertynilaidan satu methodganda(). Semua instance yang dibuat dari class ini memiliki bentuk yang sama, dan V8 memanfaatkan kesamaan bentuk tersebut untuk mengoptimasi akses property. - Baris 11 (
mapdengan arrow function): methodmapmemanggilnew Bilangan(n).ganda()untuk tiap elemen array. V8 mengeksekusi callback ini berulang kali, sehingga callback menjadi kandidat optimasi JIT. - Baris 13-14 (
process.versions.v8): objectprocessmenyediakan informasi runtime; propertyversions.v8menampilkan versi V8 yang tertanam pada Node.js yang sedang berjalan.
Pola Object yang Stabil Membantu Optimasi #
Salah satu konsekuensi praktis dari arsitektur V8: bentuk object yang konsisten membantu optimasi, sedangkan bentuk yang berubah-ubah memicu deoptimisasi. V8 mengelompokkan object dengan susunan property yang sama ke dalam struktur internal yang seragam; ketika kita menambah property di tengah jalan atau mengganti-ganti tipe nilai, kelompok tersebut pecah dan hasil optimasi sebelumnya dibuang.
function totalHarga(harga) {
return harga.awal - harga.diskon;
}
// Semua argument punya bentuk identik: awal lalu diskon.
const promoMinggu = { awal: 150000, diskon: 25000 };
const promoBulan = { awal: 300000, diskon: 50000 };
console.log(totalHarga(promoMinggu) + totalHarga(promoBulan));Versi V8 di Dalam Node.js #
Setiap versi major Node.js menautkan satu versi V8 tertentu — pada Node.js 24 yang dipakai Course ini, V8 berada di versi 13.6, sebagaimana tercetak pada output demo di atas. Konsekuensinya penting untuk kita pahami: fitur bahasa JavaScript baru tersedia di Node.js hanya setelah versi V8 yang membawanya ikut tertanam, dan pembaruan besar itu terjadi pada garis rilis major baru. Untuk memeriksa versi ini di komputer mana pun, kita cukup membaca process.versions.v8 atau modul node:v8 yang juga menyediakan alat bantu seperti statistik memori heap — keduanya terdokumentasi di API node:v8
dan API process
.
V8 mengurus satu hal besar: mengeksekusi JavaScript dengan cepat. Pertanyaan berikutnya pun muncul — siapa yang membaca berkas app.mjs dari disk, menjadwalkan timer, dan menangani ratusan koneksi jaringan sekaligus? Jawabannya ada pada kerja sama V8 dengan libuv dan event loop, topik yang kita bedah pada Chapter Bagaimana Node.js Bekerja.