Bayangkan toko kecil yang menghitung pajak di sepuluh tempat berbeda. Jika perhitungan harga * 11 / 100 disalin-tempel ke sepuluh lokasi, satu perubahan tarif berarti menyunting sepuluh baris — dan satu baris yang terlewat menghasilkan angka yang tidak konsisten. Function declaration menyelesaikan masalah ini: blok kode diberi nama sekali, lalu dipanggil berkali-kali dengan data yang berbeda. Inilah bentuk definisi function yang paling klasik di JavaScript dan menjadi fondasi seluruh section ini — bentuk yang akan kita tulis paling sering sampai arrow function diperkenalkan di chapter ketujuh.

Chapter ini menjalankan satu berkas contoh di Node yang memuat empat eksperimen: memanggil function sebelum definisinya, mempertukarkan urutan dua function, mendefinisikan function di dalam function, dan memanggil nama yang memang tidak pernah dideklarasikan. Dari sana kita membaca aturan hoisting yang membuat semua itu mungkin.

Anatomi Function Declaration #

Bentuknya selalu sama: kata kunci function, diikuti nama, lalu daftar parameter di dalam tanda kurung, dan badan function di dalam kurung kurawal. Nama function mengikuti konvensi penamaan yang sudah kita pelajari — umumnya kata kerja atau frasa kerja, ditulis camelCase, misalnya sapa, hitungLuas, verifikasiPendaftaran. Function yang sudah bernama dipanggil cukup dengan menuliskan namanya diikuti tanda kurung: sapa("Budi"). Tanda kurung inilah pembeda yang penting untuk dibaca dengan teliti: sapa tanpa tanda kurung merujuk ke nilai function-nya, sedangkan sapa() berarti memanggil dan menjalankannya.

Ketika Node memuat berkas, engine tidak langsung mengeksekusi baris demi baris secara naif. Pada tahap persiapan, seluruh function declaration terlebih dahulu didaftarkan: namanya dipasangkan dengan badan kodenya. Barulah setelah itu eksekusi baris-per-baris dimulai. Mekanisme ini disebut hoisting — “pengangkatan” — dan inilah penyebab perilaku yang akan kita buktikan sekarang.

Perbandingannya dengan deklarasi variabel yang sudah kita pelajari di section Dasar Pemrograman membantu memetakan perilaku:

DeklarasiSaat berkas dimuatDibaca sebelum barisnya dieksekusi
function nama() {}nama + badan terdaftar penuhberjalan normal
var xwadah dibuat tanpa isiundefined
let / constwadah terkunci (temporal dead zone)ReferenceError

Tiga baris tabel itu menjelaskan mengapa function declaration begitu toleran: ia satu-satunya yang terangkat lengkap dengan isinya, bukan sekadar wadahnya.

Empat Eksperimen di Node #

Berkas berikut menguji empat hal sekaligus. Perhatikan urutannya: pemanggilan ditulis lebih dulu, definisinya menyusul di bawah:

// deklarasi-fungsi.js
// Membuktikan hoisting pada function declaration langsung di Node.

// --- 1. pemanggilan sebelum definisi tetap berhasil ---
sapa("Budi"); // dipanggil sebelum baris definisinya

function sapa(nama) {
  console.log("Halo, " + nama + "!");
}

sapa("Siti"); // pemanggilan setelah definisi, hasilnya sama

// --- 2. urutan dua function declaration bebas dipertukarkan ---
console.log("Luas 5 x 3 =", hitungLuas(5, 3));

function hitungLuas(panjang, lebar) {
  return panjang * lebar; // return dibahas penuh di chapter Return Values
}

// --- 3. declaration di dalam function terangkat ke atas function itu ---
function laporan() {
  tampilkanStatus(); // dipanggil sebelum definisi di dalam function

  function tampilkanStatus() {
    console.log("Status: laporan selesai dicetak");
  }
}
laporan();

// --- 4. nama yang memang tidak dideklarasikan tetap error ---
try {
  kirimEmail("[email protected]");
} catch (error) {
  console.log(error.name, "-", error.message);
}

Output yang Dihasilkan:

text
Halo, Budi!
Halo, Siti!
Luas 5 x 3 = 15
Status: laporan selesai dicetak
ReferenceError - kirimEmail is not defined

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 5 (sapa("Budi");): pemanggilan ini ditulis sebelum definisi sapa, tetapi tidak memunculkan error. Pada tahap persiapan, sapa sudah terdaftar lengkap dengan badannya, sehingga baris ini dapat langsung menjalankannya.
  2. Baris 7–9 (function sapa(nama) { ... }): definisi function declaration. Posisinya di bawah pemanggil tidak menjadi masalah karena pendaftaran dilakukan sebelum eksekusi.
  3. Baris 14 dan 16–18 (console.log(... hitungLuas(5, 3)); lalu definisinya): urutan yang sama diterapkan pada function kedua. Hasil 15 menunjukkan pemanggilan berjalan normal.
  4. Baris 21–28 (function laporan() { ... } dan isinya): hoisting juga bekerja di dalam function. tampilkanStatus dideklarasikan di tengah laporan, tetapi terangkat ke puncak scope laporan, sehingga baris 22 yang memanggilnya lebih dahulu tetap berhasil.
  5. Baris 31–35 (try { kirimEmail(...) } catch ...): kirimEmail tidak pernah dideklarasikan di mana pun. Tanpa hoisting yang menyelamatkan, pembacaan nama tak dikenal memunculkan ReferenceError: kirimEmail is not defined. Blok try-catch di sini hanya menjaga program tetap berjalan; pembahasan penuhnya menunggu di section Error Handling.

Kesimpulan keempat eksperimen: hoisting membuat urutan definisi function declaration bebas. Function bisa dipanggil dari baris mana pun dalam scope-nya, bahkan sebelum teks definisinya dibaca. Perlu digarisbawahi pula bahwa pendaftaran ini bekerja per scope: function yang dideklarasikan di dalam laporan hanya terdaftar di lingkar laporan, bukan di seluruh berkas — batas yang akan kita pelajari tertib di chapter Scope.

Aturan Penamaan dan Deklarasi Ganda #

Ada dua aturan kecil yang melengkapi gambaran di atas. Pertama, function declaration wajib punya nama — bentuk tanpa nama ditolak saat berkas dimuat. Jika berkas berikut dijalankan:

js
function () {} // declaration tanpa nama

Node berhenti sebelum satu baris pun dieksekusi:

text
SyntaxError: Function statements require a function name

Kedua, mendeklarasikan dua function dengan nama sama tidak ditolak. Deklarasi kedua menimpa yang pertama:

js
function sapa() {
  console.log("versi pertama");
}
function sapa() {
  console.log("versi kedua");
}
sapa();

Output yang Dihasilkan:

text
versi kedua

Tidak ada error, tidak ada peringatan — deklarasi pertama hanya digantikan. Perilaku inilah alasan nama function yang sama di satu scope harus dihindari: pemanggil mungkin menjalankan versi yang bukan niat kita.

Pemanggilan Silang yang Hanya Bisa Terjadi Berkat Hoisting #

Ada satu kebutuhan nyata yang tidak bisa diselesaikan tanpa hoisting: dua function yang saling memanggil. Jika cekGenap memanggil cekGanjil dan sebaliknya, salah satunya pasti didefinisikan setelah dipanggil — dan pada function declaration itu tetap sah:

js
function cekGenap(n) {
  if (n === 0) {
    return true;
  }
  return cekGanjil(n - 1);
}

function cekGanjil(n) {
  if (n === 0) {
    return false;
  }
  return cekGenap(n - 1);
}

console.log(cekGenap(10));
console.log(cekGanjil(7));

Output yang Dihasilkan:

text
true
true

Kedua function saling melempar pekerjaan: cekGenap(10) memanggil cekGanjil(9), yang memanggil cekGenap(8), dan seterusnya hingga mencapai n === 0. Karena kedua nama terdaftar sejak berkas dimuat, urutan teks definisi tidak relevan. Bila kedua function ini ditulis sebagai function expression, urutan penulisannya malah menjadi masalah yang harus diatur manual — perbandingan yang akan kita buktikan di chapter berikutnya.

Function declaration hanyalah salah satu cara menciptakan function. Chapter berikutnya menunjukkan cara kedua yang jauh lebih luas pemakaiannya: function disimpan sebagai nilai dalam variabel, lengkap dengan perbedaan pentingnya terhadap hoisting. Kedua cara itu akan terus kita pakai bergantian sepanjang course ini, dan memilih di antara keduanya menjadi keputusan kecil yang sering kita ambil setiap kali menulis kode baru.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

deklarasi-fungsi.js

Unduh berkas
javascript
// deklarasi-fungsi.js
// Membuktikan hoisting pada function declaration langsung di Node.

// --- 1. pemanggilan sebelum definisi tetap berhasil ---
sapa("Budi"); // dipanggil sebelum baris definisinya

function sapa(nama) {
  console.log("Halo, " + nama + "!");
}

sapa("Siti"); // pemanggilan setelah definisi, hasilnya sama

// --- 2. urutan dua function declaration bebas dipertukarkan ---
console.log("Luas 5 x 3 =", hitungLuas(5, 3));

function hitungLuas(panjang, lebar) {
  return panjang * lebar; // return dibahas penuh di chapter Return Values
}

// --- 3. declaration di dalam function terangkat ke atas function itu ---
function laporan() {
  tampilkanStatus(); // dipanggil sebelum definisi di dalam function

  function tampilkanStatus() {
    console.log("Status: laporan selesai dicetak");
  }
}
laporan();

// --- 4. nama yang memang tidak dideklarasikan tetap error ---
try {
  kirimEmail("[email protected]");
} catch (error) {
  console.log(error.name, "-", error.message);
}