1. Percabangan yang Bersih dengan Pola Guard Clause (Early Return)
Percabangan if / else if / else dan switch adalah fondasi logika program. Namun, pemula sering menulis if bersarang terlalu dalam (nested arrow anti-pattern) yang sulit dibaca:
// BURUK: Bersarang terlalu dalam sehingga inti logika terkubur di paling tengah
function prosesPesananLama(pesanan) {
if (pesanan !== null && pesanan !== undefined) {
if (pesanan.barang.length > 0) {
if (pesanan.sudahDibayar) {
return `Pesanan #${pesanan.id} siap dikirim.`;
} else {
return "Menunggu pembayaran.";
}
} else {
return "Keranjang pesanan kosong.";
}
} else {
return "Data pesanan tidak ditemukan.";
}
}
// LEBIH BAIK: Gunakan Guard Clauses (kembalikan kasus gagal di awal fungsi!)
function prosesPesanan(pesanan) {
if (!pesanan) return "Data pesanan tidak ditemukan.";
if (pesanan.barang.length === 0) return "Keranjang pesanan kosong.";
if (!pesanan.sudahDibayar) return "Menunggu pembayaran.";
return `Pesanan #${pesanan.id} siap dikirim.`;
}
Dengan pola Guard Clause, semua kondisi tidak valid ditangani dan dihentikan di bagian atas fungsi, sehingga jalur sukses utama (happy path) berada di tingkat indentasi paling luar yang bersih.
2. Perulangan Modern: for...of vs for Klasik vs for...in
JavaScript menyediakan beberapa cara melakukan perulangan, namun masing-masing memiliki peruntukan yang tegas:
| Jenis Perulangan | Sintaks | Gunakan Untuk Apa? |
|---|---|---|
for...of | for (const item of daftar) | Pilihan utama untuk mengiterasi nilai di dalam Array, String, Map, Set, atau NodeList DOM. Mendukung break, continue, dan await! |
for klasik | for (let i = 0; i < n; i++) | Saat Anda benar-benar membutuhkan hitungan indeks angka manual (misalnya melompat 2 indeks sekaligus i += 2 atau berjalan mundur). |
Object.entries() + for...of | for (const [kunci, nilai] of Object.entries(obj)) | Pilihan terbaik untuk mengiterasi pasangan kunci dan nilai dari sebuah Objek ({}). |
for...in | for (const kunci in obj) | Hindari untuk Array! for...in mengiterasi kunci properti string (termasuk properti yang diwarisi dari rantai prototipe). |
const modulKelas = ["Cara Web Bekerja", "Struktur HTML", "Teks Semantik"];
// 1. Mengiterasi nilai Array dengan for...of
for (const judul of modulKelas) {
console.log(judul);
}
// 2. Jika Anda butuh nomor indeks SEKALIGUS nilainya di for...of, gunakan .entries():
for (const [indeks, judul] of modulKelas.entries()) {
console.log(`Modul ${indeks + 1}: ${judul}`);
}
3. Fungsi sebagai Warga Kelas Satu (First-Class Functions)
Di JavaScript, fungsi bukanlah struktur kaku yang terpisah dari data, melainkan **warga kelas satu (first-class citizens)**. Artinya, sebuah fungsi dapat:
- Disimpan di dalam variabel atau properti objek (
const hitung = function() {}). - Dikirimkan sebagai argumen ke fungsi lain (disebut Callback Function).
- Dikembalikan (returned) sebagai nilai hasil dari fungsi lain (disebut Higher-Order Function).
Tiga Cara Mendefinisikan Fungsi dan Perbedaannya
// 1. Function Declaration (Mengalami hoisting penuh: bisa dipanggil sebelum baris deklarasinya)
function hitungTotal(harga, jumlah = 1) {
return harga * jumlah;
}
// 2. Function Expression (Disimpan ke variabel const; tidak bisa dipanggil sebelum baris ini)
const formatRupiah = function (nominal) {
return `Rp${nominal.toLocaleString("id-ID")}`;
};
// 3. Arrow Function (Ringkas; ekspresi satu baris otomatis mengembalikan nilai tanpa kata kunci return)
const hitungPajak = (nominal, tarif = 0.11) => nominal * tarif;
// Catatan: Jika Arrow Function langsung mengembalikan objek literal {}, bungkus dengan tanda kurung ()!
const buatPengguna = (nama, peran) => ({ nama, peran, aktif: true });
Perbedaan Teknis Penting: function Biasa vs Arrow Function (=>)
Arrow function bukan sekadar singkatan penulisan dari function biasa. Ada tiga perbedaan perilaku fundamental:
- Tidak memiliki ikatan
thissendiri: Arrow function mewarisi nilaithisdari cakupan (lexical scope) di sekitarnya. Ini membuat arrow function sangat ideal untuk callback (di dalamsetTimeout,.map(), atauaddEventListenerdi dalam class), tetapi tidak cocok untuk metode objek literal yang perlu mengaksesthis.propertiobjek tersebut. - Tidak dapat dipanggil dengan kata kunci
new(bukan constructor). - Tidak memiliki objek implisit
arguments(sebagai gantinya, gunakan rest parameters...argsyang jauh lebih modern).
4. Parameter Bawaan (Default Parameters) dan Rest Parameters (...)
A. Parameter Bawaan yang Dievaluasi Saat Pemanggilan
Nilai bawaan parameter hanya digunakan jika argumen tidak dikirim atau bernilai undefined (jika Anda mengirim null, maka nilai null tetap dipakai):
function buatKoneksi(host = "localhost", port = 5432) {
return `postgres://${host}:${port}/kodeloka`;
}
console.log(buatKoneksi()); // "postgres://localhost:5432/kodeloka"
console.log(buatKoneksi("db.internal")); // "postgres://db.internal:5432/kodeloka"
console.log(buatKoneksi(undefined, 6432)); // "postgres://localhost:6432/kodeloka"
B. Mengumpulkan Sisa Argumen dengan Rest Parameters (...nama)
Sintaks tiga titik (...) pada parameter terakhir sebuah fungsi mengumpulkan seluruh sisa argumen yang dikirimkan ke dalam sebuah Array asli:
function catatLog(tingkat, ...pesan) {
const gabungPesan = pesan.join(" | ");
return `[${tingkat.toUpperCase()}] ${gabungPesan}`;
}
console.log(catatLog("info", "Server menyala", "Port 8080", "Mode produksi"));
// Keluaran: "[INFO] Server menyala | Port 8080 | Mode produksi"
Latihan Praktik: Membuat Kalkulator Tagihan Fleksibel
Buatlah berkas fungsi-tagihan.js lalu jalankan dengan Node.js:
- Buat fungsi
hitungTagihanAkhir(diskonPersen = 0, ...daftarHargaBarang)yang menggunakan guard clause (mengembalikan0jikadaftarHargaBarangkosong). - Gunakan perulangan
for...ofuntuk menjumlahkan seluruh harga di dalamdaftarHargaBarang. - Kurangi total dengan persentase diskon, tambahkan PPN 11%, dan kembalikan objek ringkasan
{ subtotal, diskonNominal, pajak, totalAkhir }.