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:
| Deklarasi | Saat berkas dimuat | Dibaca sebelum barisnya dieksekusi |
|---|---|---|
function nama() {} | nama + badan terdaftar penuh | berjalan normal |
var x | wadah dibuat tanpa isi | undefined |
let / const | wadah 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:
Halo, Budi!
Halo, Siti!
Luas 5 x 3 = 15
Status: laporan selesai dicetak
ReferenceError - kirimEmail is not definedMari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 5 (
sapa("Budi");): pemanggilan ini ditulis sebelum definisisapa, tetapi tidak memunculkan error. Pada tahap persiapan,sapasudah terdaftar lengkap dengan badannya, sehingga baris ini dapat langsung menjalankannya. - Baris 7–9 (
function sapa(nama) { ... }): definisi function declaration. Posisinya di bawah pemanggil tidak menjadi masalah karena pendaftaran dilakukan sebelum eksekusi. - Baris 14 dan 16–18 (
console.log(... hitungLuas(5, 3));lalu definisinya): urutan yang sama diterapkan pada function kedua. Hasil15menunjukkan pemanggilan berjalan normal. - Baris 21–28 (
function laporan() { ... }dan isinya): hoisting juga bekerja di dalam function.tampilkanStatusdideklarasikan di tengahlaporan, tetapi terangkat ke puncak scopelaporan, sehingga baris 22 yang memanggilnya lebih dahulu tetap berhasil. - Baris 31–35 (
try { kirimEmail(...) } catch ...):kirimEmailtidak pernah dideklarasikan di mana pun. Tanpa hoisting yang menyelamatkan, pembacaan nama tak dikenal memunculkanReferenceError: kirimEmail is not defined. Bloktry-catchdi 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:
function () {} // declaration tanpa nama
Node berhenti sebelum satu baris pun dieksekusi:
SyntaxError: Function statements require a function nameKedua, mendeklarasikan dua function dengan nama sama tidak ditolak. Deklarasi kedua menimpa yang pertama:
function sapa() {
console.log("versi pertama");
}
function sapa() {
console.log("versi kedua");
}
sapa();Output yang Dihasilkan:
versi keduaTidak 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:
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:
true
trueKedua 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// 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);
}