Hoisting JavaScript: Kenapa Sebagian Kode Jalan Sebelum Kamu Menulisnya

Hoisting menjelaskan kenapa fungsi bisa dipanggil sebelum didefinisikan, kenapa var bernilai undefined bukannya error, dan kenapa let melempar error di temporal dead zone. Pahami apa yang sebenarnya 'dinaikkan' JavaScript — dan apa yang tidak.

Diterbitkan 7 Agustus 20268 menit bacaOleh ACY Partner Indonesia
Hoisting JavaScript — deklarasi dinaikkan ke atas scope-nya
300 × 250Slot Iklan TersediaPasang iklan Anda di sini

Cepat atau lambat, kamu bakal nulis kode JavaScript yang harusnya nggak jalan tapi malah jalan — sebuah fungsi yang kamu panggil beberapa baris sebelum didefinisikan, eh ternyata tetap berfungsi normal. Atau justru kebalikannya: sebuah variabel yang mencetak undefined padahal kamu yakin nilainya pasti ada atau setidaknya melempar error. Dua-duanya berakar pada satu mekanisme yang namanya kedengaran agak dramatis: hoisting.

Hoisting adalah perilaku di mana JavaScript seolah-olah memindahkan deklarasi ke bagian paling atas scope-nya sebelum kode mulai dijalankan. Kata kuncinya “seolah-olah” — nggak ada yang benar-benar pindah tempat di file kamu. Tapi memahami model ini menjelaskan satu kelas bug yang sering bikin bingung, dan topik ini pas banget jadi pasangan dari scope dan closure, yang sebaiknya sudah kamu pahami dulu sebelum lanjut.

Apa itu hoisting sebenarnya

Saat engine JavaScript menjalankan kodemu, dia nggak benar-benar membaca baris demi baris dari awal. Dia bekerja dalam dua tahap. Pertama ada fase pembuatan (creation phase), di mana engine memindai scope yang sedang aktif dan mencatat setiap deklarasi yang dia temukan — setiap var, setiap function, setiap let, const, dan class. Baru setelah itu fase eksekusi dimulai, menjalankan tiap pernyataan dari atas ke bawah.

Karena deklarasi sudah dicatat lebih dulu, sebuah nama bisa dikenali oleh engine bahkan sebelum baris tempat kamu menulisnya. Itulah inti triknya. Tapi ada jebakannya: dikenali bukan berarti sudah punya nilai — dan tiap kata kunci berperilaku sangat berbeda soal ini.

console.log(pesan);   // undefined — dikenali, tapi belum punya nilai
var pesan = "Halo";
console.log(pesan);   // "Halo"

Model berpikir yang gampang: bayangkan engine menulis ulang kodemu sehingga bagian deklarasi naik ke atas, sementara bagian penugasan nilai tetap di tempat asalnya.

// Kira-kira begini cara engine memandangnya:
var pesan;            // deklarasi dinaikkan ke atas
console.log(pesan);   // undefined
pesan = "Halo";       // penugasan tetap di tempatnya
console.log(pesan);   // "Halo"

Itulah sebabnya log pertama menghasilkan undefined, bukan error “not defined”. Namanya memang sudah ada; nilainya saja yang belum datang.

Bagaimana var dinaikkan

Variabel yang dideklarasikan pakai var akan dinaikkan dan langsung diisi undefined selama fase pembuatan. Jadi kamu bisa memakainya sebelum baris deklarasinya tanpa bikin program crash — kamu cuma akan dapat undefined sampai baris penugasannya dijalankan.

function sapa() {
  console.log(nama);    // undefined (bukan error)
  var nama = "John Doe";
  console.log(nama);    // "John Doe"
}
sapa();

Ada keunikan var yang kedua dan penting kamu tahu: dia bersifat function-scoped, bukan block-scoped. Sebuah var yang dideklarasikan di dalam if atau for akan dinaikkan ke atas seluruh fungsi, bukan cuma ke atas blok tempat dia ditulis.

function cek(skor) {
  if (skor > 50) {
    var hasil = "lulus";
  }
  console.log(hasil);   // "lulus" kalau skor > 50, selain itu undefined — tapi tidak pernah error
}

Walaupun hasil ditulis di dalam blok if, var mengangkatnya ke atas fungsi cek, sehingga console.log terakhir tetap bisa melihatnya. Kebocoran semacam inilah yang justru ingin dicegah oleh let dan const.

Hoisting var menyembunyikan bug

Karena membaca var sebelum penugasannya cuma diam-diam mengembalikan undefined alih-alih melempar error, salah ketik atau salah logika bisa lewat begitu saja tanpa ketahuan. Kamu lihat undefined, lalu mengira datanya memang nggak ada, dan akhirnya mencari bug di tempat yang salah. Kegagalan yang senyap seperti ini jadi salah satu alasan utama kenapa JavaScript modern menyarankanmu menjauhi var sama sekali.

let dan const: temporal dead zone

let dan const juga dinaikkan — ini sering bikin kaget orang yang terlanjur dengar “cuma var yang di-hoisting.” Bedanya, keduanya tidak langsung diisi undefined. Mereka tetap dicatat selama fase pembuatan, tapi statusnya belum terisi sampai engine benar-benar mencapai baris deklarasinya. Sentuh sebelum titik itu, dan kamu langsung dapat ReferenceError.

console.log(jumlah);   // ReferenceError: Cannot access 'jumlah' before initialization
let jumlah = 5;

Jeda itu — dari awal scope sampai baris deklarasi, di mana namanya sudah ada tapi belum boleh disentuh — disebut temporal dead zone, atau TDZ. Disebut “temporal” karena ini soal waktu (rentang saat eksekusi berlangsung), bukan soal wilayah fisik di dalam file.

{
  // TDZ untuk `total` mulai di sini
  // console.log(total);  // ReferenceError kalau baris ini diaktifkan
  const total = 100;      // TDZ berakhir — `total` sekarang bisa dipakai
  console.log(total);     // 100
}

Ini disengaja, dan justru jadi kelebihan, bukan kekurangan. Alih-alih undefined yang senyap seperti pada var, TDZ membuat kesalahanmu langsung berbunyi keras. Kamu tahu masalahnya tepat di titik kamu memakai variabel terlalu dini, bukannya pusing mengejar undefined misterius yang muncul di tiga fungsi yang berbeda.

Kenapa TDZ justru menolongmu

Temporal dead zone mengubah satu kelas bug yang senyap jadi pesan error yang jelas. Kalau kamu lihat “Cannot access ‘x’ before initialization”, itu bukan kelemahan JavaScript — itu bahasa pemrogramannya memberi tahu kamu persis di mana kamu memakai variabel sebelum mendeklarasikannya. Cara membetulkannya: pindahkan deklarasi ke atas penggunaan pertamanya. Ini satu lagi alasan untuk lebih memilih let dan const ketimbang var.

Hoisting fungsi: deklarasi vs ekspresi

Di bagian fungsi inilah hoisting terasa nyaris seperti sulap — tapi hanya satu jenis fungsi yang dapat perlakuan penuh.

Sebuah function declaration (bentuk function nama() {}) dinaikkan secara utuh: baik namanya maupun isi tubuhnya. Itulah sebabnya kamu bisa memanggilnya sebelum dia muncul di file.

sapaHalo();   // "Hai!" — jalan, padahal definisinya ada di bawah

function sapaHalo() {
  console.log("Hai!");
}

Engine mencatat seluruh fungsi itu selama fase pembuatan, jadi begitu eksekusi sampai ke sapaHalo(), fungsinya sudah benar-benar siap.

Sebuah function expression — yaitu menugaskan fungsi ke sebuah variabel — ceritanya beda. Di sini, yang dinaikkan cuma variabelnya, mengikuti aturan kata kunci yang kamu pakai. Nilai fungsinya sendiri belum tersedia sampai baris penugasannya dijalankan.

sampaiJumpa();   // TypeError: sampaiJumpa is not a function

var sampaiJumpa = function () {
  console.log("Sampai jumpa!");
};

Karena sampaiJumpa dideklarasikan pakai var, namanya dinaikkan dan diisi undefined. Memanggil undefined() jelas melempar TypeError — perhatikan, ini bukan ReferenceError, karena namanya memang ada; cuma belum berupa fungsi. Coba ganti jadi let atau const, kamu malah akan kena TDZ dan dapat ReferenceError karena memakainya terlalu dini.

Arrow function berperilaku persis seperti function expression, soalnya dia selalu ditugaskan ke sebuah variabel:

halo();   // ReferenceError (const masih di dalam TDZ)

const halo = () => console.log("Halo");

Deklarasi vs ekspresi, sekilas pandang

Sebuah function declaration (function foo() {}) dinaikkan utuh — nama dan isinya — jadi bisa kamu panggil sebelum ditulis. Sebuah function expression atau arrow function (const foo = () => {}) cuma menaikkan variabelnya, mengikuti aturan var/let/const — jadi memanggilnya terlalu dini akan memberi undefined/TypeError (var) atau ReferenceError (let/const). Kalau ragu, definisikan dulu sebelum dipakai.

Satu contoh utuh yang merangkum semuanya

Berikut satu potongan kode yang menguji semua aturan di atas. Coba tebak hasil tiap baris dulu sebelum membaca komentarnya:

function demo() {
  console.log(a);        // undefined — var dinaikkan, belum ada nilai
  // console.log(b);     // ReferenceError — b masih di dalam TDZ
  deklarasi();           // "Aku sebuah deklarasi" — dinaikkan utuh
  // ekspresi();         // TypeError — ekspresi masih undefined di sini

  var a = 1;
  let b = 2;

  function deklarasi() {
    console.log("Aku sebuah deklarasi");
  }

  var ekspresi = function () {
    console.log("Aku sebuah ekspresi");
  };

  console.log(a, b);     // 1 2 — keduanya sudah terisi sekarang
  ekspresi();            // "Aku sebuah ekspresi" — sekarang sudah jadi fungsi
}

demo();

Dibaca dari atas ke bawah, semua kejutannya hilang: a sudah ada tapi masih undefined, b belum boleh disentuh di TDZ-nya, deklarasi siap seketika, dan ekspresi baru bisa dipanggil setelah baris penugasannya jalan. Tiap hasil mengikuti langsung dari kata kunci apa yang memperkenalkan nama tersebut.

Cara menghindari kejutan hoisting

Kamu sebenarnya jarang ingin mengandalkan hoisting — kode yang rapi itu terbaca sesuai urutan dia dijalankan. Beberapa kebiasaan ini bisa menjauhkanmu dari masalah:

  • Utamakan const, lalu let. Hindari var. Ini perbaikan paling besar dampaknya. let dan const itu block-scoped dan dilindungi TDZ, jadi akses dini yang nggak sengaja akan gagal dengan keras alih-alih senyap. Kalau kamu sudah paham pemilihan kata kunci dari artikel variabel dan tipe data, kamu sebenarnya sudah jalan jauh.
  • Deklarasikan variabel di awal scope-nya (atau tepat sebelum dipakai pertama kali). Dengan begitu nggak ada jeda yang bisa salah dibaca, dan kodemu terbaca sesuai urutan eksekusi.
  • Definisikan fungsi sebelum kamu memanggilnya. Function declaration secara teknis memang membolehkan urutan terbalik, tapi membaca kode yang memanggil sesuatu sebelum didefinisikan itu lebih susah buat manusia, walau engine-nya santai-santai saja.
  • Anggap undefined yang muncul tiba-tiba sebagai petunjuk. Kalau sebuah nilai jadi undefined padahal kamu mengharapkan data, sebuah var yang dibaca sebelum penugasannya adalah tersangka klasik — coba periksa urutannya.

Inti praktisnya

Kamu nggak perlu menghafal seluk-beluk dua fase engine untuk menulis kode yang kokoh. Cukup pakai const dan let, deklarasikan dulu sebelum dipakai, dan hoisting praktis berhenti jadi masalah. Alasan kamu tetap perlu memahaminya adalah untuk saatnya kamu membaca kode orang lain yang penuh var, atau ketika kamu kena ReferenceError yang nggak kamu duga — saat itulah kamu langsung tahu apa yang sedang terjadi.

Penutup

Hoisting adalah proses engine mencatat deklarasi sebelum menjalankan kodemu, sehingga beberapa nama bisa dipakai sebelum barisnya sendiri — tapi dengan cara yang berbeda-beda:

  • var dinaikkan dan diisi undefined, jadi membacanya terlalu dini menghasilkan undefined (tanpa error). Dia juga function-scoped, sehingga bisa bocor keluar dari blok.
  • let dan const dinaikkan tapi belum terisi, terjebak di temporal dead zone sampai barisnya sendiri. Mengaksesnya terlalu dini melempar ReferenceError — sebuah kegagalan yang keras dan justru menolong.
  • Function declaration dinaikkan utuh (bisa dipanggil sebelum didefinisikan). Function expression dan arrow function cuma menaikkan variabelnya, jadi memanggilnya terlalu dini akan melempar error.
  • Pertahanan praktisnya sederhana: pakai const/let, deklarasikan sebelum dipakai, definisikan fungsi sebelum memanggilnya. Lakukan itu dan hoisting akan tenggelam ke latar belakang, persis di tempat seharusnya.

Begitu hoisting benar-benar nyantol, banyak momen “lho, kok bisa begitu?” akan terjawab dengan sendirinya. Ini juga jadi batu loncatan menuju topik yang lebih dalam, seperti nilai dari sebuah fungsi dan bagaimana rantai scope menyelesaikan sebuah nama — detail yang mengubah tebak-tebakan jadi pemahaman.

Tag:javascriptfrontendhoistingvar let constmenengah
728 × 90Slot Iklan TersediaPasang iklan Anda di sini

Artikel Terkait

Lihat Semua Artikel

Artikel yang Mungkin Kamu Suka

Apa Itu Framework Frontend? — chip kode dengan tag komponen
Frontend / Dasar

Apa Itu Framework Frontend?

Penjelasan ramah untuk pemula tentang apa itu framework frontend, kenapa mereka ada, masalah apa yang mereka selesaikan dibanding JavaScript biasa, dan kapan kamu memang sebaiknya memakainya.

20 Sep 20268 menit baca
Sampul fundamentals frontend bertuliskan Apa Itu Frontend dan HTML + CSS + JS
Frontend / Dasar

Apa Itu Frontend Development?

Panduan ramah pemula tentang frontend development: bagian website yang kamu lihat dan klik secara langsung. Pahami cara kerja HTML, CSS, dan JavaScript, serta bedanya frontend dengan backend.

20 Sep 20268 menit baca
Ilustrasi sampul untuk Apa yang Dikerjakan Frontend Developer
Frontend / Dasar

Apa yang Dikerjakan Frontend Developer: Panduan untuk Pemula

Penjelasan ramah pemula tentang apa yang sebenarnya dikerjakan frontend developer setiap hari: mengubah desain jadi antarmuka yang berfungsi, membangun UI yang bisa dipakai ulang, sampai urusan responsif, aksesibilitas, dan kecepatan.

20 Sep 20269 menit baca
Kartu judul bertuliskan Styling dan Interaktivitas di atas latar biru gelap ACY Partner
Frontend / Dasar

Cara Kerja Styling dan Interaktivitas

Penjelasan ramah pemula soal bagaimana CSS menyulap HTML polos jadi tampilan rapi, dan bagaimana JavaScript menambahkan perilaku — pola berpikir struktur, gaya, perilaku saat membangun halaman web.

20 Sep 20268 menit baca
Tiga lapisan front-end menyatu di satu halaman web
Frontend / Dasar

Cara HTML, CSS, dan JavaScript Bekerja Bersama

Panduan praktis untuk pemula: bikin satu tombol kecil, beri struktur dengan HTML, tampilan dengan CSS, dan perilaku dengan JavaScript, lalu lihat ketiga lapisan ini bekerja sama di halaman nyata.

20 Sep 20268 menit baca
Sampul ikhtisar praktik terbaik frontend
Frontend / Dasar

Ikhtisar Praktik Terbaik Frontend

Peta ringkas tentang apa itu frontend yang baik — HTML bermakna, CSS rapi, JavaScript secukupnya, desain responsif, performa, dan progressive enhancement, lengkap dengan arahan untuk mendalaminya.

20 Sep 20268 menit baca