Closure JavaScript Adalah: Penjelasan Sederhana + Contoh Kode
Closure JavaScript adalah fungsi yang mengingat variabel dari lingkup luarnya walau lingkup itu sudah selesai. Pelajari cara kerja, contoh, dan kegunaannya.
Pembuat Hyper Sheets & BelajarWeb.dev
Closure adalah sebuah fungsi di JavaScript yang “mengingat” dan tetap bisa mengakses variabel dari lingkup (scope) di luarnya, bahkan setelah fungsi luar tempat variabel itu dibuat sudah selesai dijalankan. Sederhananya: ketika kamu membuat fungsi di dalam fungsi lain, fungsi yang di dalam membawa serta “kenangan” tentang variabel-variabel di sekitarnya ke mana pun ia dibawa. Kenangan inilah yang disebut closure. Konsep ini sering bikin pemula pusing karena terdengar abstrak, padahal sebenarnya kamu sudah memakainya tanpa sadar setiap kali menulis event handler atau callback.
Di panduan ini kamu akan memahami closure benar-benar dari nol: apa itu scope (fondasi yang wajib dulu), bagaimana sebuah fungsi bisa “mengingat” variabel luar, contoh closure yang paling klasik, kegunaannya di dunia nyata (data privat, function factory, event handler), sampai jebakan closure di dalam loop yang paling sering menjebak pemula. Semua dengan contoh kode berbahasa Indonesia yang bisa langsung kamu coba di console browser.
Fondasi Dulu: Apa Itu Scope?
Sebelum bisa paham closure, kamu harus paham scope, yaitu aturan tentang di mana saja sebuah variabel bisa diakses. Anggap scope sebagai “wilayah jangkauan” sebuah variabel.
Aturan dasarnya: fungsi di dalam bisa melihat variabel di luar, tapi tidak sebaliknya.
let namaGlobal = "Budi" // variabel di scope luar (global)
function sapa() {
let pesan = "Halo" // variabel di scope dalam (fungsi)
console.log(pesan, namaGlobal) // bisa akses keduanya
}
sapa() // "Halo Budi"
console.log(pesan) // ERROR! pesan tidak ada di sini
Perhatikan baris terakhir: pesan hanya hidup di dalam fungsi sapa. Di luar fungsi, ia tidak ada. Inilah yang disebut lexical scope, sebuah fungsi menentukan variabel apa yang bisa ia akses berdasarkan di mana ia ditulis dalam kode, bukan dari mana ia dipanggil. Kalau kamu masih baru dengan konsep fungsi dan scope, ada baiknya perkuat dulu fondasinya lewat panduan JavaScript untuk pemula sebelum lanjut.
Nah, closure lahir dari satu pertanyaan menarik: apa yang terjadi kalau sebuah fungsi mengembalikan fungsi lain yang masih memakai variabel luar tadi?
Cara Kerja Closure: Fungsi yang Mengingat
Mari kita lihat contoh closure paling klasik. Perhatikan baik-baik:
function buatSapaan(salam) {
// 'salam' adalah variabel di scope luar
return function (nama) {
// fungsi di dalam ini memakai 'salam'
return `${salam}, ${nama}!`
}
}
const sapaFormal = buatSapaan("Selamat pagi")
const sapaSantai = buatSapaan("Halo")
console.log(sapaFormal("Pak Andi")) // "Selamat pagi, Pak Andi!"
console.log(sapaSantai("Rina")) // "Halo, Rina!"
Ada sesuatu yang aneh (dan ajaib) di sini. Fungsi buatSapaan sudah selesai dijalankan begitu kita memanggilnya di baris const sapaFormal = .... Menurut logika biasa, variabel salam seharusnya sudah “hilang” karena fungsinya sudah kelar.
Tapi tidak. Ketika kita memanggil sapaFormal("Pak Andi"), ia masih ingat bahwa salam bernilai "Selamat pagi". Fungsi bagian dalam membawa serta variabel salam ke mana pun ia pergi. Itulah closure: fungsi dalam “menutup” (close over) variabel dari lingkungan tempat ia dibuat.
Yang lebih menakjubkan: sapaFormal dan sapaSantai masing-masing punya “kenangan” salam yang berbeda dan terpisah. Satu ingat "Selamat pagi", satu ingat "Halo". Mereka tidak saling mengganggu.
Contoh Paling Terkenal: Fungsi Counter
Contoh yang paling sering dipakai untuk menjelaskan closure adalah pembuat penghitung (counter). Ini menunjukkan kekuatan sesungguhnya dari closure: menyimpan data yang privat dan awet antar pemanggilan.
function buatCounter() {
let count = 0 // variabel privat, tidak bisa diakses dari luar
return function () {
count++ // fungsi dalam mengubah count
return count
}
}
const counter = buatCounter()
console.log(counter()) // 1
console.log(counter()) // 2
console.log(counter()) // 3
Perhatikan bahwa count tidak di-reset ke 0 setiap kali counter() dipanggil. Nilainya bertahan: 1, lalu 2, lalu 3. Ini karena count tidak dibuat ulang, ia hidup di dalam closure yang sama, dan fungsi yang dikembalikan terus mengubah nilai yang itu-itu juga.
Dan yang paling penting: count tidak bisa disentuh dari luar. Tidak ada cara untuk menulis counter.count = 100. Satu-satunya cara mengubah count adalah lewat fungsi yang kita kembalikan. Inilah cara JavaScript membuat variabel benar-benar “privat”, sebuah teknik yang sangat berguna.
Coba Sendiri: Setiap Counter Punya Ingatan Sendiri
Teori paling gampang dicerna kalau kamu lihat langsung. Di bawah ini, setiap kotak adalah satu pemanggilan buatCounter() yang terpisah. Klik count++ di masing-masing kotak, dan perhatikan bahwa angka mereka naik secara independen, bukti bahwa setiap closure memegang variabel count pribadinya sendiri. Coba juga tombol “Buat counter baru” untuk menambah closure baru.
Coba Sendiri
Setiap kotak di bawah adalah hasil pemanggilan buatCounter(). Klik tombolnya dan perhatikan: setiap counter menyimpan angka count-nya sendiri secara terpisah. Itulah bukti bahwa closure "mengingat" variabel pribadinya.
count pribadi = 0count pribadi = 0function buatCounter() {
let count = 0 // variabel pribadi
return function () {
count++ // "diingat" oleh closure
return count
}
}Setiap kotak membuktikan hal yang sama: closure bukan berbagi satu count bersama, melainkan masing-masing menyimpan salinannya sendiri. Kamu bisa mendalami konsep ini secara interaktif dengan latihan bertahap di pelajaran closures di Hyper Sheets yang bisa langsung dikerjakan di browser tanpa setup.
Perbedaan Scope Biasa dan Closure
Banyak pemula bingung membedakan “sekadar mengakses variabel luar” dengan “closure”. Ini bagian penting yang membedakan artikel ini dari sekadar hafalan. Kuncinya ada pada waktu.
Perhatikan tabel perbandingan berikut:
| Aspek | Scope biasa | Closure |
|---|---|---|
| Kapan variabel luar diakses | Selama fungsi luar masih berjalan | Bahkan setelah fungsi luar selesai |
| Umur variabel | Hilang saat fungsi luar selesai | Tetap hidup selama fungsi dalam masih ada |
| Dibuat lewat | Pemanggilan fungsi biasa | Fungsi yang mengembalikan/menyimpan fungsi lain |
| Contoh khas | function() { console.log(x) } dipanggil langsung |
return function() { ... } lalu disimpan ke variabel |
Intinya begini: scope adalah aturan tentang variabel apa yang bisa dilihat sebuah fungsi. Closure adalah apa yang terjadi ketika fungsi dalam “dibawa keluar” dari fungsi luar dan tetap membawa akses ke variabel luar tersebut. Semua closure memakai scope, tapi baru disebut closure ketika fungsi dalam hidup lebih lama daripada fungsi luar yang melahirkannya.
// Ini SCOPE biasa, fungsi dipakai lalu selesai
function hitung() {
let x = 10
console.log(x) // 10, lalu x langsung hilang
}
hitung()
// Ini CLOSURE, fungsi dalam "kabur" keluar & masih ingat x
function simpan() {
let x = 10
return () => x // fungsi ini dibawa keluar
}
const ingat = simpan()
console.log(ingat()) // 10, x masih hidup lewat closure!
Kegunaan Closure di Dunia Nyata
Closure bukan cuma trik akademis, kamu memakainya terus tanpa sadar. Berikut tiga kegunaan paling umum.
1. Menyimpan data privat. Seperti contoh counter tadi, closure adalah cara JavaScript menyembunyikan variabel agar tidak bisa diubah sembarangan dari luar. Ini pola yang dipakai banyak library untuk melindungi state internalnya.
2. Function factory (pabrik fungsi). Kamu bisa membuat fungsi yang menghasilkan fungsi lain dengan konfigurasi berbeda:
function buatPengali(faktor) {
return (angka) => angka * faktor
}
const kaliDua = buatPengali(2)
const kaliSepuluh = buatPengali(10)
console.log(kaliDua(5)) // 10
console.log(kaliSepuluh(5)) // 50
kaliDua dan kaliSepuluh masing-masing “mengingat” nilai faktor yang berbeda lewat closure. Kalau kamu belum familiar dengan sintaks panah => di atas, belajar React dari nol juga banyak memakai pola arrow function seperti ini.
3. Event handler dan callback. Setiap kali kamu memasang event listener yang memakai variabel dari luar, closure lah yang membuatnya bekerja:
function pasangTombol(namaUser) {
const tombol = document.querySelector("#sapa")
tombol.addEventListener("click", () => {
// closure: fungsi ini ingat 'namaUser' walau
// pasangTombol sudah lama selesai dijalankan
alert(`Halo, ${namaUser}!`)
})
}
Jebakan Closure di Dalam Loop
Ada satu jebakan closure yang paling sering menjebak pemula, dan pantas kamu tahu sejak awal. Perhatikan kode ini yang memakai var di dalam loop:
// Kode yang bikin bingung
for (var i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), 100)
}
// Output: 4, 4, 4 (bukan 1, 2, 3!)
Kenapa hasilnya 4, 4, 4 dan bukan 1, 2, 3? Karena var hanya punya satu variabel i yang dipakai bersama oleh seluruh iterasi. Ketiga fungsi setTimeout membentuk closure atas variabel i yang sama. Saat timer akhirnya berjalan (setelah loop selesai), nilai i sudah menjadi 4.
Solusinya sederhana di JavaScript modern: ganti var dengan let.
// Kode yang benar
for (let i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), 100)
}
// Output: 1, 2, 3 ✅
Kenapa let berhasil? Karena let membuat variabel i yang baru untuk setiap iterasi. Jadi setiap closure setTimeout menutup atas i yang berbeda-beda, persis seperti setiap kotak counter di playground tadi punya count sendiri.
Kenapa variabel count di dalam function buatCounter() tidak ikut hilang setelah buatCounter() selesai dijalankan?
FAQ Seputar Closure JavaScript
Apa itu closure dengan bahasa paling sederhana?
Closure adalah fungsi yang “mengingat” variabel dari tempat ia dibuat, walaupun ia dijalankan di tempat lain atau di waktu yang jauh kemudian. Bayangkan sebuah fungsi yang membawa ransel berisi variabel-variabel dari lingkungan asalnya, ransel itu selalu ikut ke mana pun fungsi dibawa, sehingga variabel di dalamnya tetap bisa diakses.
Apa bedanya scope dan closure?
Scope adalah aturan tentang variabel apa saja yang bisa diakses oleh sebuah fungsi berdasarkan di mana fungsi itu ditulis. Closure adalah fenomena yang terjadi ketika sebuah fungsi dalam “dibawa keluar” dari fungsi luarnya (misalnya lewat return) dan tetap mempertahankan akses ke variabel luar tersebut, bahkan setelah fungsi luar selesai. Singkatnya: closure memanfaatkan scope, tapi baru disebut closure kalau fungsi dalam hidup lebih lama dari fungsi luarnya.
Apakah semua fungsi di JavaScript adalah closure?
Secara teknis, hampir setiap fungsi di JavaScript membentuk closure karena setiap fungsi punya akses ke scope di sekitarnya. Namun istilah “closure” biasanya baru dipakai secara bermakna ketika sebuah fungsi benar-benar memanfaatkan variabel dari scope luar yang seharusnya sudah “hilang”. Jadi walau semua fungsi punya potensi closure, kita hanya menyebutnya closure ketika sifat “mengingat” itu benar-benar dipakai.
Kapan sebaiknya saya memakai closure?
Kamu memakai closure paling sering untuk tiga hal: menyimpan data privat yang tidak boleh diubah sembarangan dari luar, membuat function factory (fungsi yang menghasilkan fungsi terkonfigurasi seperti buatPengali), dan menulis event handler atau callback yang perlu mengingat data tertentu. Dalam praktik, kamu akan sering memakainya tanpa sadar setiap kali menulis addEventListener atau setTimeout.
Apakah closure menyebabkan memory leak?
Bisa, tapi jarang jadi masalah jika kamu menulis kode dengan wajar. Karena closure membuat variabel tetap hidup di memori selama fungsi dalamnya masih dipegang, variabel yang besar (misalnya array raksasa) bisa tetap menempati memori lebih lama dari yang diharapkan. Solusinya cukup dengan tidak menyimpan referensi fungsi yang sudah tidak dipakai, dan membersihkan event listener yang tidak lagi diperlukan.
Apakah closure sama dengan arrow function?
Tidak, keduanya konsep yang berbeda. Arrow function (=>) hanyalah cara penulisan fungsi yang lebih ringkas, sedangkan closure adalah perilaku “mengingat variabel luar” yang bisa dimiliki oleh fungsi jenis apa pun, baik fungsi biasa, arrow function, maupun function expression. Arrow function memang sering dipakai untuk membuat closure karena ringkas, tapi keduanya bukan hal yang sama.
Kesimpulan dan Langkah Berikutnya
Closure adalah salah satu konsep JavaScript yang terdengar rumit di awal, tapi sebenarnya sangat logis begitu kamu memahami scope. Intinya cuma satu kalimat: fungsi di JavaScript mengingat variabel dari tempat ia dibuat, dan membawanya ke mana pun ia pergi. Dari situlah lahir data privat, function factory, dan event handler yang bekerja seperti sihir.
Kalau kamu ingin benar-benar memantapkan konsep ini, cara terbaik adalah menulis kode sendiri dan melihat langsung bagaimana variabel “diingat”. Coba kerjakan latihan closures interaktif di Hyper Sheets yang memandu kamu langkah demi langkah di browser. Setelah nyaman dengan closure, konsep-konsep lanjutan seperti this, callback async, dan cara kerja state di React akan terasa jauh lebih mudah dicerna, karena hampir semuanya berdiri di atas fondasi yang baru kamu pelajari di sini. Mulai dari yang kecil, tulis kode setiap hari, dan biarkan pemahaman closure ini tumbuh alami seiring latihan.