Chapter sebelumnya menyimpan function dengan namanya sendiri. Cara itu sah, tetapi JavaScript punya pandangan yang lebih luas: function adalah nilai — satu jenis nilai yang dapat disimpan dalam variabel, disalin ke variabel lain, dan dikirim ke function lain. Pandangan inilah yang nanti menjelaskan mengapa callback, array method, dan hampir seluruh pola modern JavaScript bekerja. Titik masuknya sederhana: function ditulis sebagai bagian dari sebuah ekspresi (expression), misalnya di sisi kanan tanda =.
Chapter ini membandingkan dua wajah function expression — yang anonim dan yang bernama — lalu membuktikan satu perbedaan mendasarnya terhadap function declaration: ekspresi tidak diangkat (hoisting), sehingga urutan penulisannya menentukan apakah program berjalan atau error.
Function Sebagai Nilai #
Dalam function expression, kata kunci function tidak harus diikuti nama. Function tanpa nama disebut anonymous function, dan nilai yang dihasilkannya boleh langsung disimpan ke dalam variabel dengan const atau let. Setelah tersimpan, variabel itu dipanggil seperti function biasa — cukup tambahkan tanda kurung dan argument.
Berkas contoh berikut mencoba empat hal: membuat function expression anonim, menyalinnya ke variabel lain, memanggilnya sebelum didefinisikan, dan membuat versi bernama:
// ekspresi-fungsi.js
// Function expression: function sebagai nilai yang disimpan dalam variabel.
// --- 1. function expression anonim ---
const hitungDiskon = function (harga, persen) {
return harga - (harga * persen) / 100;
};
console.log("Harga akhir 25000 diskon 20%:", hitungDiskon(25000, 20));
console.log("Tipe nilainya:", typeof hitungDiskon);
// --- 2. function adalah nilai: bisa disalin ke variabel lain ---
const hitungPotongan = hitungDiskon; // alias, bukan tiruan kedua
console.log("Via alias 100000 diskon 10%:", hitungPotongan(100000, 10));
console.log("Nama function:", hitungDiskon.name);
// --- 3. expression tidak terangkat: panggil sebelum definisi = error ---
try {
console.log(cekUmur(17));
} catch (error) {
console.log(error.name, "-", error.message);
}
const cekUmur = function (usia) {
return usia >= 17 ? "boleh mendaftar" : "belum cukup umur";
};
console.log("Setelah definisi:", cekUmur(17));
// --- 4. named function expression: nama hidup di dalam saja ---
const periksa = function verifikasi(nilai) {
console.log("Dari dalam, verifikasi =", typeof verifikasi);
return "nilai " + nilai + " diperiksa";
};
console.log(periksa(8));
console.log("Nama function:", periksa.name);
try {
console.log(verifikasi);
} catch (error) {
console.log(error.name, "-", error.message);
}Output yang Dihasilkan:
Harga akhir 25000 diskon 20%: 20000
Tipe nilainya: function
Via alias 100000 diskon 10%: 90000
Nama function: hitungDiskon
ReferenceError - Cannot access 'cekUmur' before initialization
Setelah definisi: boleh mendaftar
Dari dalam, verifikasi = function
nilai 8 diperiksa
Nama function: verifikasi
ReferenceError - verifikasi is not definedMari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 5–7 (
const hitungDiskon = function (harga, persen) { ... };): function tanpa nama dibuat sebagai nilai, lalu diikat kehitungDiskon. Perhatikan titik koma di akhir kurung kurawal — ekspresi diakhiri seperti statementconstbiasa. - Baris 9 (
console.log("Tipe nilainya:", typeof hitungDiskon);):typeofmelaporkanfunction— bukti bahwa nilai yang disimpan memang bertipe function, berdampingan dengan string, number, dan tipe lain yang sudah kita kenal. - Baris 12–14 (
const hitungPotongan = hitungDiskon;): yang disalin adalah referensi ke function yang sama, bukan tiruan kedua. Keduanya menghitung hal yang sama, dan propertinamemenampilkanhitungDiskon— engine menyimpulkan nama dari nama variabelnya (inferred name). - Baris 17–21 (
try { console.log(cekUmur(17)); }):cekUmurdideklarasikan denganconstdi baris 23. Karenaconstberada di temporal dead zone sebelum barisnya dieksekusi, pemanggilan di baris 18 memunculkanReferenceError: Cannot access 'cekUmur' before initialization. - Baris 29–34 (
const periksa = function verifikasi(nilai) { ... }): inilah named function expression — function expression yang diberi namaverifikasi. Nama itu terbaca dari dalam badannya sendiri (baris 30 melaporkanfunction), tetapi tidak bocor keluar: pembacaanverifikasidi baris 37 memunculkanReferenceError: verifikasi is not defined.
Dua perilaku di poin 4 dan 5 adalah inti chapter ini. Kita bedah lebih lanjut satu per satu.
Ekspresi Tidak Diangkat #
Function declaration terangkat penuh — nama dan badannya terdaftar sebelum eksekusi. Function expression mengikuti aturan variabel: const dan let diangkat tanpa nilai, dan tidak boleh dibaca sebelum baris deklarasinya dieksekusi. Dengan kata lain, pada function expression, urutan menulis dan urutan berpikir harus sama: definisikan dulu, panggil kemudian.
Tabel berikut merangkum perbedaannya dengan declaration:
| Aspek | Function declaration | Function expression |
|---|---|---|
| Bentuk | function sapa() {} | const sapa = function () {} |
| Hoisting | penuh — bisa dipanggil sebelum definisi | tidak — mengikuti aturan const/let |
| Panggil sebelum definisi | berhasil | ReferenceError |
| Wajib bernama | ya | tidak — anonim diperbolehkan |
| Nama tersimpan | nama eksplisit | disimpulkan dari nama variabel |
Praktik yang mengikuti aturan ini: kelompokkan definisi function expression di bagian atas berkas atau di atas pemakaiannya, dan beri komentar pemisah bila berkasnya panjang. Bila sebuah function memang perlu dipanggil dari banyak tempat — termasuk dari atas berkas — function declaration tetap pilihan yang sah; keduanya boleh hidup berdampingan dalam satu proyek, dan banyak kode nyata memakai declaration untuk utilitas besar serta expression untuk function kecil yang lekat dengan pemakaiannya.
Anonymous vs Named: Kapan Memakai yang Mana #
Mayoritas function expression di kode nyata ditulis anonim karena namanya sudah diwakili variabel penampungnya. Versi bernama tetap berguna pada dua situasi: ketika function perlu memanggil dirinya sendiri dari dalam (nama terbaca di dalam badan — kita akan memakai pola ini di chapter Recursion), dan ketika pembacaan stack trace membutuhkan nama yang bermakna.
Properti name pada berkas contoh memperlihatkan keduanya: function anonim mendapat nama disimpulkan dari variabelnya (hitungDiskon), sedangkan named function expression memakai nama eksplisitnya (verifikasi).
Sebagai pelengkap, layak mengetahui jejak var di kode lama. Function expression yang disimpan lewat var mengikuti aturan var: wadahnya terangkat tanpa isi, sehingga typeof sebelum assignment tidak error melainkan melaporkan undefined:
console.log(typeof sapaLama);
var sapaLama = function () {
console.log("halo dari var");
};
sapaLama();Output yang Dihasilkan:
undefined
halo dari varBaris pertama mencetak undefined — bukan ReferenceError seperti kasus cekUmur — karena var mengangkat wadahnya. Pemanggilan setelah assignment berjalan normal. Pola ini banyak dijumpai di kode lama; untuk kode baru, const memberi jaminan urutan yang lebih tegas.
Function expression juga muncul di tempat-tempat yang tidak selalu terlihat sebagai “deklarasi”: sebagai argument langsung di dalam pemanggilan function lain (pola callback yang dibahas di chapter kedelapan), dan sebagai isi properti object. Keduanya memakai bentuk yang sama — function sebagai nilai — hanya tempat penampungnya yang berbeda.
Sekarang kita punya function yang tersimpan sebagai nilai. Nilai semacam itu baru berguna penuh bila menerima data dari luar — itulah urusan parameter dan argument, topik chapter berikutnya. Dari situ pula satu istilah yang akan kita temui terus menjadi jelas: bila function expression inilah bentuk umum function-as-value di kode modern, arrow function pada chapter ketujuh nanti hanyalah penulisan ringkas dari ide yang sama — tanda => menggantikan kata function, tanpa mengubah statusnya sebagai nilai.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
ekspresi-fungsi.js
Unduh berkas// ekspresi-fungsi.js
// Function expression: function sebagai nilai yang disimpan dalam variabel.
// --- 1. function expression anonim ---
const hitungDiskon = function (harga, persen) {
return harga - (harga * persen) / 100;
};
console.log("Harga akhir 25000 diskon 20%:", hitungDiskon(25000, 20));
console.log("Tipe nilainya:", typeof hitungDiskon);
// --- 2. function adalah nilai: bisa disalin ke variabel lain ---
const hitungPotongan = hitungDiskon; // alias, bukan tiruan kedua
console.log("Via alias 100000 diskon 10%:", hitungPotongan(100000, 10));
console.log("Nama function:", hitungDiskon.name);
// --- 3. expression tidak terangkat: panggil sebelum definisi = error ---
try {
console.log(cekUmur(17));
} catch (error) {
console.log(error.name, "-", error.message);
}
const cekUmur = function (usia) {
return usia >= 17 ? "boleh mendaftar" : "belum cukup umur";
};
console.log("Setelah definisi:", cekUmur(17));
// --- 4. named function expression: nama hidup di dalam saja ---
const periksa = function verifikasi(nilai) {
console.log("Dari dalam, verifikasi =", typeof verifikasi);
return "nilai " + nilai + " diperiksa";
};
console.log(periksa(8));
console.log("Nama function:", periksa.name);
try {
console.log(verifikasi);
} catch (error) {
console.log(error.name, "-", error.message);
}