Siti meminta satu laporan dari data pendaftaran lokakarya: daftar nama peserta yang sudah lunas untuk sesi js-dasar. Permintaan yang sama bisa ditulis dengan dua gaya yang benar-benar berbeda. Gaya pertama menyusun langkah secara eksplisit — siapkan wadah kosong, telusuri data satu per satu, uji tiap data, masukkan yang lolos. Gaya kedua langsung menyatakan hasilnya: dari seluruh pendaftaran, ambil yang lunas dan sesuai sesi, kembalikan namanya saja, rangkai dengan koma.
Dua gaya itu punya nama. Imperative programming menyatakan bagaimana hasil dicapai, langkah demi langkah. Declarative programming menyatakan apa hasil yang diinginkan, dan membiarkan mekanisme eksekusinya ditangani alat yang tersedia. Keduanya sah, keduanya berjalan, dan keduanya punya tempatnya — yang berubah adalah apa yang pembaca lihat saat membuka kode. Chapter ini menulis tugas yang sama dengan kedua gaya pada data yang sama, membandingkan kembaliannya yang identik, lalu menilai dari mana perbedaan keterbacaannya datang.
Imperatif: Menyusun Langkah demi Langkah #
Berkas berikut menjalankan kedua gaya pada satu dataset pendaftaran. Bagian pertama adalah versi imperatif — dua loop, satu variabel penampung, satu uji kondisi:
// imperatif-vs-deklaratif.js
// Dua gaya penulisan, satu dataset, hasil identik.
const pendaftaran = [
{ nama: "Budi", sesi: "js-dasar", lunas: true },
{ nama: "Siti", sesi: "js-dasar", lunas: false },
{ nama: "Dewi", sesi: "js-lanjut", lunas: true },
{ nama: "Adi", sesi: "js-dasar", lunas: true },
{ nama: "Rina", sesi: "js-lanjut", lunas: true },
];
// --- 1. imperatif: menyusun langkah demi langkah ---
let pesertaLunas = [];
for (let i = 0; i < pendaftaran.length; i++) {
const data = pendaftaran[i];
if (data.lunas === true && data.sesi === "js-dasar") {
pesertaLunas.push(data.nama);
}
}
console.log(pesertaLunas);
let label = "Peserta: ";
for (let i = 0; i < pesertaLunas.length; i++) {
label = label + pesertaLunas[i];
if (i < pesertaLunas.length - 1) {
label = label + ", ";
}
}
console.log(label);
// --- 2. deklaratif: menyatakan hasil yang diinginkan ---
const labelDeklaratif =
"Peserta: " +
pendaftaran
.filter((p) => p.lunas === true && p.sesi === "js-dasar")
.map((p) => p.nama)
.join(", ");
console.log(labelDeklaratif);
// --- 3. kebutuhan berubah: rantai deklaratif ikut menyesuaikan ---
const jumlahPeserta = pendaftaran
.filter((p) => p.lunas === true && p.sesi === "js-dasar")
.length;
console.log(jumlahPeserta);
const namaKapital = pendaftaran
.filter((p) => p.lunas === true && p.sesi === "js-dasar")
.map((p) => p.nama.toUpperCase());
console.log(namaKapital);Output yang Dihasilkan:
[ 'Budi', 'Adi' ]
Peserta: Budi, Adi
Peserta: Budi, Adi
2
[ 'BUDI', 'ADI' ]Mari kita telaah baris-baris pentingnya:
- Baris 4–10 (dataset bersama): lima entri pendaftaran dengan tiga properti — nama, sesi, dan status pembayaran. Dataset ini menjadi titik ukur yang adil: kedua gaya memproses data yang sama persis.
- Baris 13–19 (loop penyaringan): wadah
pesertaLunasdideklarasikan dulu, loop menelusuri setiap elemen lewat index, dan kondisi di baris 16 memutuskan mana yang di-push. Semua kendali ada di tangan pembaca kode — dan semua tanggung jawabnya juga: index, batas, wadah. - Baris 22–29 (loop perangkaian): label dirakit satu nama per putaran, dengan pemeriksaan
i < pesertaLunas.length - 1agar koma tidak menempel di akhir. Perhatikan berapa banyak baris yang bicara tentang mekanisme perangkaian, bukan tentang isi laporannya. - Baris 32–38 (versi deklaratif): maksud yang sama ditulis satu rantai —
filtermenyatakan kriteria,mapmenyatakan bentuk keluaran,joinmenyatakan pemisah. Tidak ada wadah, tidak ada index, tidak ada uji panjang; mekanismenya dititipkan ke method yang sudah teruji. - Baris 41–44 (kebutuhan baru: hitung): permintaan berubah menjadi jumlah peserta. Versi deklaratif cukup mengganti ekor rantai dengan
.length— kriteriafilteryang sama dipakai kembali. - Baris 46–49 (kebutuhan lain: kapital): permintaan berikutnya mengubah bentuk keluaran. Yang diganti hanya
map-nya; bagian menyaring tidak disentuh sama sekali.
Satu catatan yang jujur tentang pengukuran: jumlah baris bukan pengganti kualitas. Versi imperatif pada berkas contoh memang lebih panjang, tetapi panjang itu datang dari mekanisme yang dinyatakan terbuka — dan ada situasi ketika kendali penuh justru dibutuhkan, misalnya saat dua daftar harus diproses sekaligus per index. Yang penting dipilih dengan sadar: gaya yang menyembunyikan mekanisme untuk alur data, gaya yang menampilkannya untuk kendali.
Keterbacaan: Apa yang Dilihat Pembaca #
Keluaran kedua gaya identik — Peserta: Budi, Adi — tetapi pengalaman membacanya berbeda. Pada versi imperatif, pembaca harus mengeksekusi kode di kepala: mengikuti index, wadah, dan urutan langkah untuk sampai pada maksud. Pada versi deklaratif, maksud muncul dari kata-katanya sendiri: filter, map, join — tiga method yang masing-masing menyatakan satu niat.
| Aspek | Imperatif | Deklaratif |
|---|---|---|
| Yang ditulis | langkah eksekusi | hasil yang diinginkan |
| State antara | wadah dan index dikelola sendiri | tidak ada; hasil tiap tahap mengalir |
| Sumber kesalahan | batas loop, index, wadah lupa diisi | kriteria di callback |
| Perubahan kebutuhan | langkah disusun ulang | rantai disambung atau dilepas |
Tabel itu bukan vonis. Gaya imperatif tetap fondasi: loop for adalah alat yang tepat ketika urutan langkah memang inti persoalannya, dan semua method array yang kita pakai pada bab-bab sebelumnya — map, filter, join — sendirinya ditulis imperatif di dalam engine JavaScript.
Ada juga cara membaca yang berbeda di antara keduanya. Loop imperatif dibaca seperti mengikuti jejak: mulai dari wadah kosong, menelusuri data, mengamati apa yang berubah di tiap putaran. Rantai deklaratif dibaca seperti membaca resep dari bahan ke hidangan: data mentah masuk dari kiri, lalu tiap tahap menyebut satu perubahan bentuk. Bila kita membaca rantai dengan suara — “dari pendaftaran, ambil yang lunas dan sesi dasar, ambil namanya, rangkai dengan koma” — kalimat itu hampir sama persis dengan permintaan Siti di pembuka chapter.
Kriteria Bernama: Rantai yang Mudah Dibaca #
Rantai pada berkas contoh mengulang callback kriteria yang sama tiga kali — sah untuk pengajaran, tetapi dalam kode nyata kriteria itu layak diberi nama:
const lunasDasar = (p) => p.lunas === true && p.sesi === "js-dasar";
console.log(pendaftaran.filter(lunasDasar).length);
console.log(pendaftaran.filter(lunasDasar).map((p) => p.nama));Output yang Dihasilkan:
2
[ 'Budi', 'Adi' ]Function lunasDasar kini menyatakan satu konsep bisnis yang bisa dibaca pemanggilnya, dan perubahan kriteria cukup terjadi di satu tempat. Inilah titik temu kedua gaya: deklaratif untuk menyatakan alur data, dan function kecil bernama — sering kali murni — untuk menyatakan aturannya.
Rantai yang sama mudah diputar ke kebutuhan lain tanpa menyentuh kriterianya. Daftar peserta sesi lanjutan, misalnya, cukup dibaca dari ujung yang berbeda:
const namaTerurut = pendaftaran
.filter((p) => p.sesi === "js-lanjut")
.map((p) => p.nama)
.toSorted();
console.log(namaTerurut);Output yang Dihasilkan:
[ 'Dewi', 'Rina' ]Kriteria berubah, bentuk keluaran berubah, dan urutannya ditambahkan — semuanya lewat penyambungan atau pelepasan tahap, bukan penulisan ulang logika.
Gaya deklaratif berdiri di atas beberapa pola yang sudah kita bangun: higher-order function, function murni, dan pipeline. Chapter terakhir section ini merapikan pola-pola itu menjadi kosa kata yang padat — currying, partial application, point-free, dan pipeline.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
imperatif-vs-deklaratif.js
Unduh berkas// imperatif-vs-deklaratif.js
// Dua gaya penulisan, satu dataset, hasil identik.
const pendaftaran = [
{ nama: "Budi", sesi: "js-dasar", lunas: true },
{ nama: "Siti", sesi: "js-dasar", lunas: false },
{ nama: "Dewi", sesi: "js-lanjut", lunas: true },
{ nama: "Adi", sesi: "js-dasar", lunas: true },
{ nama: "Rina", sesi: "js-lanjut", lunas: true },
];
// --- 1. imperatif: menyusun langkah demi langkah ---
let pesertaLunas = [];
for (let i = 0; i < pendaftaran.length; i++) {
const data = pendaftaran[i];
if (data.lunas === true && data.sesi === "js-dasar") {
pesertaLunas.push(data.nama);
}
}
console.log(pesertaLunas);
let label = "Peserta: ";
for (let i = 0; i < pesertaLunas.length; i++) {
label = label + pesertaLunas[i];
if (i < pesertaLunas.length - 1) {
label = label + ", ";
}
}
console.log(label);
// --- 2. deklaratif: menyatakan hasil yang diinginkan ---
const labelDeklaratif =
"Peserta: " +
pendaftaran
.filter((p) => p.lunas === true && p.sesi === "js-dasar")
.map((p) => p.nama)
.join(", ");
console.log(labelDeklaratif);
// --- 3. kebutuhan berubah: rantai deklaratif ikut menyesuaikan ---
const jumlahPeserta = pendaftaran
.filter((p) => p.lunas === true && p.sesi === "js-dasar")
.length;
console.log(jumlahPeserta);
const namaKapital = pendaftaran
.filter((p) => p.lunas === true && p.sesi === "js-dasar")
.map((p) => p.nama.toUpperCase());
console.log(namaKapital);