Pada chapter Callbacks di section Functions, Budi sudah mengirim function ke function lain: daftar tugas diproses oleh satu function yang memanggil ulang function yang dikirimkan padanya. Saat itu, pemakaian itu terasa seperti trik khusus. Padahal yang sedang bekerja adalah salah satu ciri utama JavaScript: function diperlakukan seperti nilai biasa — bisa disimpan di variabel, dikirim sebagai argumen, dan dikembalikan dari function lain.

Function yang menerima function sebagai argumen, atau mengembalikan function baru, disebut higher-order function. Konsep ini adalah motor penggerak paradigma functional di JavaScript: map dan filter di section Arrays bekerja karena sifat ini, dan pola-pola di chapter selanjutnya — composition, memoization, currying — dibangun di atasnya. Sebagian besar pengalaman kita sejauh ini sebenarnya sudah melibatkan higher-order function; chapter ini hanya membongkar penutupnya, lalu menulis satu dari nol.

Function adalah Nilai #

Sebelum menyebut istilahnya, mari kita pastikan fondasinya. Pernyataan function tidak wajib menyebut nama; hasilnya adalah nilai seperti angka atau string, hanya saja nilai ini bisa dipanggil. Karena itu function boleh dijadikan argumen, dan parameter yang menerimanya hanyalah variabel biasa yang kebetulan memegang function:

js
const dobel = function (angka) {
  return angka * 2;
};

console.log(dobel(7));
console.log(typeof dobel);

Output yang Dihasilkan:

text
14
function

typeof menjawab "function" — tipe miliknya sendiri, tetapi perilakunya sebagai nilai sama seperti lainnya: ia disimpan, dikirim, dan dikembalikan. Arrow function dari section Functions mempersingkat penulisannya, dan bentuk itulah yang dipakai sepanjang chapter ini.

Cara termudah membaca higher-order function: callback adalah titik penyisipan perilaku. Pemilik function sudah menyiapkan kerangka kerjanya — menelusuri elemen, mengumpulkan hasil — dan detail “apa yang dilakukan pada setiap elemen” disisipkan lewat argumen. Kerangka yang sama bisa menerima ratusan perilaku berbeda tanpa diubah satu baris pun.

map dan filter sebagai Higher-Order Function #

Dua method yang selama ini kita pakai ternyata memenuhi definisi tadi. map menerima callback lalu memanggilnya untuk setiap elemen; filter menerima callback yang bertugas sebagai uji. Keduanya tidak peduli isi callbacknya — yang mereka butuhkan adalah sebuah function:

// pembuat-fungsi.js
// Higher-order function: menerima function, mengembalikan function.

// --- 1. map dan filter: HOF yang menerima callback ---
const hargaMenu = [12000, 18000, 8000, 24000];

const hargaDenganPajak = hargaMenu.map((harga) => harga + (harga * 10) / 100);
console.log(hargaDenganPajak);

const hargaTerjangkau = hargaMenu.filter((harga) => harga <= 15000);
console.log(hargaTerjangkau);

// --- 2. menulis HOF sendiri: function sebagai parameter ---
function terapkanKeSemua(daftar, operasi) {
  const hasil = [];
  for (const nilai of daftar) {
    hasil.push(operasi(nilai));
  }
  return hasil;
}

console.log(terapkanKeSemua(hargaMenu, (harga) => harga * 2));

// --- 3. function builder: mengembalikan function baru ---
function buatPengali(faktor) {
  return function (angka) {
    return angka * faktor;
  };
}

const kalikanDua = buatPengali(2);
const kalikanSepuluh = buatPengali(10);
console.log(kalikanDua(21));
console.log(kalikanSepuluh(21));

// --- 4. builder dipakai sebagai callback: filter yang bisa diatur ---
function buatFilterMaksimal(batas) {
  return function (angka) {
    return angka <= batas;
  };
}

const diBawahSedang = buatFilterMaksimal(15000);
console.log(hargaMenu.filter(diBawahSedang));
console.log(hargaMenu.filter(buatFilterMaksimal(10000)));

Output yang Dihasilkan:

text
[ 13200, 19800, 8800, 26400 ]
[ 12000, 8000 ]
[ 24000, 36000, 16000, 48000 ]
42
210
[ 12000, 8000 ]
[ 8000 ]

Mari kita telaah baris-baris pentingnya:

  1. Baris 7 (hargaMenu.map(...)): map menerima callback (harga) => harga + (harga * 10) / 100 dan memanggilnya sekali per elemen. Kembalian callback dikumpulkan menjadi array baru [ 13200, 19800, 8800, 26400 ] — perhitungan pajak dititipkan ke function yang dikirim.
  2. Baris 10 (hargaMenu.filter(...)): filter memakai callback-nya sebagai uji benar-salah; hanya 12000 dan 8000 yang lolos syarat <= 15000.
  3. Baris 14–20 (terapkanKeSemua): inilah higher-order function versi kita sendiri. Parameter operasi hanyalah variabel biasa yang memegang function, dan baris 17 memanggilnya dengan operasi(nilai) — persis seperti yang dilakukan map. Kode ini menyingkap mekanisme di balik layar: map tidak melakukan apa pun di luar memanggil function yang dikirim.
  4. Baris 22 (pemanggilan HOF): callback (harga) => harga * 2 dikirim sebagai argumen kedua, dan hasilnya [ 24000, 36000, 16000, 48000 ] — setiap harga digandakan.
  5. Baris 25–29 (buatPengali): higher-order function jenis kedua — yang mengembalikan function. Function dalam menutup parameter faktor dari function luarnya, mekanisme closure dari section Functions.
  6. Baris 31–34 (dua turunan): kalikanDua dan kalikanSepuluh lahir dari template yang sama, tetapi masing-masing membawa nilai faktor yang berbeda: 42 dan 210 untuk masukan 21.
  7. Baris 37–45 (builder sebagai callback): buatFilterMaksimal menggabungkan dua arah sekaligus — ia dikembalikan sebagai function, lalu dikirim ke filter sebagai callback. Batas 15000 menghasilkan dua harga, batas 10000 hanya satu.

Sebagai tambahan, callback dari map sebenarnya menerima lebih dari satu argumen: nilai, index, dan array asalnya. Selama ini kita hanya memakai yang pertama, tetapi argumen kedua berguna saat posisi elemen ikut menentukan hasil:

js
const labelHarga = hargaMenu.map((harga, urutan) => `${urutan + 1}. Rp${harga}`);
console.log(labelHarga[0]);
console.log(labelHarga.length);

Output yang Dihasilkan:

text
1. Rp12000
4

Callback dua parameter itu tetap satu function biasa — map tinggal mengisi urutan dengan posisi elemen yang sedang diproses.

Function Builder: Function yang Dikonfigurasi Sekali #

Baris 31–45 memperlihatkan pola yang patut diberi nama: function builder — function yang tugasnya membuat function lain dengan konfigurasi yang sudah terpasang. Nama pemanggilnya biasa diawali buat..., dan isi yang berbeda-beda (faktor, batas) dikunci sejak saat pembuatan, bukan saat pemanggilan:

js
function buatPenyapa(sapaan) {
  return (nama) => sapaan + ", " + nama + "!";
}

const sapaKasir = buatPenyapa("Selamat pagi");
console.log(sapaKasir("Budi"));
console.log(sapaKasir("Siti"));

Output yang Dihasilkan:

text
Selamat pagi, Budi!
Selamat pagi, Siti!

Perhatikan bahwa builder tidak mengulang logika — sapaKasir dipanggil dua kali dengan sapaan yang sama, karena konfigurasi sapaan hanya ditulis sekali. Pola factory ini kita temui lebih dulu di chapter Closures; bedanya sekarang, kita membacanya sebagai higher-order function: function yang kembaliannya adalah function.

Builder semacam ini juga menyimpan konfigurasi tanpa variabel global. Nilai sapaan hidup di closure milik sapaKasir, sehingga dua builder dengan konfigurasi berbeda bisa hidup berdampingan tanpa saling menimpa — keuntungan yang sama yang diperlihatkan dua penghitung pada chapter Closures.

Kapan Function Biasa, Kapan Builder #

Dua bentuk pada berkas contoh mengajak sebuah pertimbangan praktis. Function biasa seperti terapkanKeSemua menjawab kebutuhan “lakukan X pada semua item”, dengan perilaku ditentukan pemanggil saat pemanggilan. Builder seperti buatFilterMaksimal menjawab kebutuhan “sediakan versi X yang sudah dikonfigurasi”, dengan perilaku dikunci sejak pembuatan.

Kaidah pemilihannya mengikuti frekuensi. Bila konfigurasi dipakai sekali, function biasa cukup — builder hanya menambah lapisan tanpa keuntungan. Bila konfigurasi yang sama dipakai di banyak tempat, builder menghasilkan nama bernama seperti hargaMember atau diBawahSedang, dan perubahan konfigurasi cukup terjadi di satu tempat pembuatannya. Pada pemakaian nyata, keduanya sering bekerja bersama: builder menyiapkan function, lalu higher-order function seperti map dan filter menjalankannya atas data — susunan yang persis kita rangkai di chapter berikutnya.

Satu function yang bisa menerima function lain membuka pintu berikutnya: memasang beberapa function kecil menjadi satu alur kerja. Itulah function composition yang dibahas di chapter selanjutnya.

Berkas Sumber

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

pembuat-fungsi.js

Unduh berkas
javascript
// pembuat-fungsi.js
// Higher-order function: menerima function, mengembalikan function.

// --- 1. map dan filter: HOF yang menerima callback ---
const hargaMenu = [12000, 18000, 8000, 24000];

const hargaDenganPajak = hargaMenu.map((harga) => harga + (harga * 10) / 100);
console.log(hargaDenganPajak);

const hargaTerjangkau = hargaMenu.filter((harga) => harga <= 15000);
console.log(hargaTerjangkau);

// --- 2. menulis HOF sendiri: function sebagai parameter ---
function terapkanKeSemua(daftar, operasi) {
  const hasil = [];
  for (const nilai of daftar) {
    hasil.push(operasi(nilai));
  }
  return hasil;
}

console.log(terapkanKeSemua(hargaMenu, (harga) => harga * 2));

// --- 3. function builder: mengembalikan function baru ---
function buatPengali(faktor) {
  return function (angka) {
    return angka * faktor;
  };
}

const kalikanDua = buatPengali(2);
const kalikanSepuluh = buatPengali(10);
console.log(kalikanDua(21));
console.log(kalikanSepuluh(21));

// --- 4. builder dipakai sebagai callback: filter yang bisa diatur ---
function buatFilterMaksimal(batas) {
  return function (angka) {
    return angka <= batas;
  };
}

const diBawahSedang = buatFilterMaksimal(15000);
console.log(hargaMenu.filter(diBawahSedang));
console.log(hargaMenu.filter(buatFilterMaksimal(10000)));