Ilustrasi mesin function dengan panah masuk dan hasil keluar

Di dua artikel terakhir kamu belajar menyimpan data, mengambil keputusan, dan mengulang pekerjaan. Sekarang ada masalah baru yang pasti kamu rasakan: kode mulai panjang dan ada bagian yang ditulis berulang-ulang. Mengecek ongkir ditulis di tiga tempat. Menghitung diskon ditulis di lima tempat. Begitu aturannya berubah, kamu harus berburu ke semua tempat itu satu per satu.

Koki profesional tidak mengulek bumbu dari nol setiap kali ada pesanan. Ia menyiapkan bumbu dasar dalam wadah-wadah kecil, siap sendok kapan pun dibutuhkan. Function adalah wadah bumbu itu: sekumpulan kode yang dibungkus, diberi nama, dan bisa dipakai ulang kapan saja. Ini juga langkah pertamamu menuju cara berpikir programmer profesional: memecah masalah besar jadi potongan-potongan kecil yang rapi.

Di akhir artikel ini kamu bisa bikin function sendiri, mengerti bedanya parameter dan argumen, paham kenapa return itu penting, dan tahu jebakan hoisting yang bikin banyak pemula garuk-garuk kepala.

Function Itu Mesin Kecil Berlabel

Bayangkan mesin pembuat jus di dapur. Kamu masukkan buah dari satu sisi, tekan tombol, jus keluar dari sisi lain. Kamu tidak perlu tahu cara kerja pisaunya, cukup tahu: masukkan buah, dapat jus.

Function bekerja sama. Kamu "memasukkan" data (disebut parameter), function mengolahnya, lalu "mengeluarkan" hasil (dengan return). Cara bikinnya:

function buatJus(buah) {
  return "Jus " + buah + " segar!";
}

let gelas1 = buatJus("mangga");
console.log(gelas1); // "Jus mangga segar!"

Bedakan dua istilah yang sering tertukar: parameter adalah "slot" yang dideklarasikan saat function dibuat (buah di baris pertama), sedangkan argumen adalah nilai nyata yang dimasukkan saat function dipanggil ("mangga" di baris pemanggilan). Parameter itu seperti lubang surat, argumen itu suratnya.

Kenapa ini rapi? Tanpa function, setiap kali butuh jus kamu menulis ulang logikanya. Dengan function, logikanya ditulis satu kali, dipakai berkali-kali. Kalau resep jusnya berubah, kamu edit satu tempat saja.

Berhenti dan tebak dulu! Apa outputnya?

function tambah(a, b) {
  return a + b;
}
let hasil = tambah(4, 7);
console.log(hasil);
console.log(tambah(10, 20));
Jawaban: 11 lalu 30. tambah(4, 7) mengembalikan 11 yang disimpan di hasil, lalu dicetak. tambah(10, 20) mengembalikan 30 yang langsung dicetak tanpa disimpan dulu. Kuncinya: return itu seperti "menyerahkan hasil keluar mesin". Nilai kembalian bisa disimpan ke variabel, bisa juga langsung dipakai.

Return: Jangan Lupa Mengeluarkan Hasilnya

Kesalahan klasik pemula: function-nya menghitung dengan benar, tapi lupa return, lalu heran kenapa hasilnya undefined.

function kali(a, b) {
  let hasil = a * b;
  // lupa return!
}
console.log(kali(3, 4)); // undefined

Analoginya: kamu memasukkan buah ke mesin jus, mesinnya berputar, tapi tidak ada gelas yang keluar. Mesinnya bekerja, hasilnya tidak diserahkan. Function tanpa return selalu menghasilkan undefined. Kalau function-mu dipakai untuk menghitung sesuatu yang hasilnya dibutuhkan di tempat lain, return itu wajib.

Ada satu pola return yang sangat umum di kode profesional dan layak kamu kenal sejak awal: early return, alias "pulang cepat". Daripada membungkus seluruh logika dalam if bertingkat, kamu mengembalikan hasil langsung untuk kasus khusus di awal function:

function cekLulus(nilai) {
  if (nilai < 0 || nilai > 100) {
    return "Nilai tidak valid";
  }
  if (nilai >= 60) {
    return "Lulus";
  }
  return "Tidak lulus";
}

Perhatikan tidak ada else sama sekali. Setiap return langsung menghentikan function, jadi kode di bawahnya otomatis hanya jalan kalau kondisi di atasnya tidak terpenuhi. Hasilnya: function yang datar dan mudah dibaca, bukan piramida if di dalam if di dalam if. Setiap kali kamu tergoda menulis else setelah if yang berisi return, ingat pola ini.

Ada juga function yang memang tidak perlu mengembalikan apa-apa, misalnya yang tugasnya cuma menampilkan sesuatu ke layar:

function sapa(nama) {
  console.log("Halo, " + nama + "!");
}
sapa("Dita"); // langsung cetak, tidak butuh return

Cara berpikirnya: tanya ke diri sendiri, "apakah hasil kerja function ini akan dipakai lagi di tempat lain?" Kalau ya, pakai return. Kalau function ini adalah "ujung tombak" yang tugasnya cuma melakukan aksi (cetak, tampilkan, simpan), tidak perlu.

Parameter Punya Nilai Cadangan

Kadang pemanggil function lupa (atau memang tidak perlu) memberi semua argumen. Tanpa persiapan, parameter yang tidak terisi jadi undefined dan bisa merusak perhitungan:

function sapa(nama) {
  return "Halo, " + nama + "!";
}
console.log(sapa()); // "Halo, undefined!" -- memalukan

Solusinya: default parameter, nilai cadangan yang dipakai kalau argumen tidak diberikan:

function sapa(nama = "teman") {
  return "Halo, " + nama + "!";
}
console.log(sapa());        // "Halo, teman!"
console.log(sapa("Dita"));  // "Halo, Dita!"

Perhatikan: nilai default cuma dipakai kalau argumennya tidak diberikan (atau undefined). Kalau pemanggil sengaja memberi null, ya null yang dipakai, bukan defaultnya. JavaScript menghormati keputusan eksplisit, sekecil apa pun.

Dua Persepsi yang Sering Salah

Pertama: "function yang bagus itu yang panjang dan mengerjakan banyak hal." Kebalik. Function yang bagus itu pendek dan mengerjakan satu hal. Kalau function-mu butuh nama seperti hitungTotalDanKirimEmailDanCetakStruk, itu tanda ia harus dipecah jadi tiga function. Aturan praktisnya: kalau kamu kesulitan memberi nama yang singkat dan jelas, function-nya kemungkinan terlalu gemuk.

Kedua: "memanggil function sebelum dideklarasikan pasti error." Tergantung cara bikinnya, dan ini jebakan hoisting yang terkenal:

sapaDeklarasi(); // BERHASIL
function sapaDeklarasi() { console.log("Halo!"); }

sapaEkspresi(); // ERROR!
const sapaEkspresi = function() { console.log("Halo!"); };

Function declaration (cara pertama) "diangkat" sepenuhnya oleh JavaScript sebelum kode dijalankan, jadi bisa dipanggil dari mana saja. Function expression (cara kedua, function disimpan di variabel) mengikuti aturan variabel: const-nya belum diisi saat dipanggil, jadi error. Praktisnya: pakai function declaration untuk function utilitas biasa, dan biasakan menulis function sebelum dipakai supaya tidak perlu menghafal aturan ini.

Berhenti dan tebak dulu! Apa outputnya?

function hitung(a, b = 5) {
  return a * b;
}
console.log(hitung(3));
console.log(hitung(3, 2));
Jawaban: 15 lalu 6. Panggilan pertama tidak memberi argumen untuk b, jadi nilai cadangan 5 dipakai: 3 * 5 = 15. Panggilan kedua memberi 2, jadi default diabaikan: 3 * 2 = 6. Kuncinya: default parameter hanya "penjaga gawang" saat argumen absen, bukan nilai yang selalu menang.

Kuis cepat: Kapan sebuah function WAJIB memakai return?

Latihan: Ubah, Jangan Cuma Dibaca

  1. Ubah kalkulator ongkir jadi function. Ambil latihan ongkir dari artikel kemarin (if else bertingkat), bungkus jadi function hitungOngkir(totalBelanja) yang me-return angka ongkirnya. Panggil tiga kali dengan total berbeda dan cetak hasilnya.
  2. Tambah parameter diskon. Ubah function ongkir tadi supaya menerima parameter kedua diskon = 0. Kalau diskon diisi, kurangi ongkirnya (tapi jangan sampai minus, pakai if di dalam function). Uji dengan dan tanpa argumen diskon.
  3. Pecah function gemuk. Tulis (atau bayangkan) satu function panjang yang menghitung total belanja + ongkir + mencetak struk. Pecah jadi tiga function kecil: hitungTotal, hitungOngkir, cetakStruk, lalu bikin function keempat yang memanggil ketiganya. Rasakan bedanya saat dibaca.

Habis Ini ke Mana?

Sekarang kamu bisa membungkus logika jadi paket rapi yang bisa dipakai ulang. Tapi ada satu masalah yang belum tersentuh: variabel yang kita pakai sejauh ini cuma bisa menyimpan satu nilai. Bagaimana kalau datanya ada 100? Bikin 100 variabel buah1, buah2, sampai buah100? Tentu tidak.

Artikel berikutnya, Array dan Object di JavaScript untuk Menyimpan Banyak Data, mengajarkan dua "gudang" serbaguna: array untuk daftar berurutan dan object untuk data berlabel. Di sanalah potongan-potongan yang kamu pelajari (variabel, loop, function) mulai terasa menyatu jadi sesuatu yang beneran powerful.