Closure di TypeScript
Dalam artikel ini, kami akan menjelaskan tentang closure di TypeScript.
YouTube Video
Closure di TypeScript
Apa itu Closure?
Closure mengacu pada kemampuan untuk mempertahankan referensi ke lingkup (lingkungan) di mana fungsi didefinisikan, bahkan jika fungsi tersebut dipanggil di luar lingkupnya. Di bawah ini, closure akan dijelaskan termasuk anotasi tipe.
Secara sederhana, closure menggabungkan sebuah fungsi dan lingkungan variabel tempat fungsi tersebut didefinisikan, sehingga memungkinkan akses ke lingkungan tersebut saat fungsi dipanggil.
Mekanisme Dasar dari Closure
Di TypeScript, ketika sebuah fungsi didefinisikan di dalam fungsi lain, akan terlihat bahwa fungsi bagian dalam dapat mengakses variabel dari fungsi luar. Berikut adalah contoh dasar closure dengan anotasi tipe.
1function outerFunction(): () => void {
2 let outerVariable: string = "I am from outer function";
3
4 function innerFunction(): void {
5 // The inner function accesses the variable of the outer function
6 console.log(outerVariable);
7 }
8
9 return innerFunction;
10}
11
12const closure: () => void = outerFunction();
13closure(); // "I am from outer function"
- Tipe pengembalian dari
outerFunction
adalah() => void
, yang berarti fungsi tersebut mengembalikan sebuah fungsi. - Tipe dari
innerFunction
secara eksplisit ditetapkan sebagaivoid
, yang berarti tidak memiliki nilai kembali.
Penggunaan dan Keuntungan Closure
Enkapsulasi Data
Di bawah ini adalah contoh closure dengan anotasi tipe untuk enkapsulasi data.
1function createCounter(): () => number {
2 let count: number = 0;
3
4 return function (): number {
5 count += 1;
6 return count;
7 };
8}
9
10const counter: () => number = createCounter();
11console.log(counter()); // 1
12console.log(counter()); // 2
13console.log(counter()); // 3
- Fungsi
createCounter
mengembalikan fungsi dengan tipe() => number
. - Variabel
count
didefinisikan sebagai tipenumber
dan dimanipulasi di dalam closure.
Fungsi Higher-Order
Closure sangat berguna saat membuat fungsi higher-order. Berikut adalah contoh fungsi higher-order dengan anotasi tipe yang jelas.
1function createMultiplier(multiplier: number): (value: number) => number {
2 return function (value: number): number {
3 return value * multiplier;
4 };
5}
6
7const double: (value: number) => number = createMultiplier(2);
8console.log(double(5)); // 10
9
10const triple: (value: number) => number = createMultiplier(3);
11console.log(triple(5)); // 15
createMultiplier
menerima argumen bertipenumber
dan mengembalikan fungsi bertipe(value: number) => number
.- Fungsi bagian dalam juga menerima
value
bertipenumber
dan mengembalikan hasil bertipenumber
.
Contoh Implementasi Closure di TypeScript
Implementasikan penghitung dengan batas rentang sebagai closure dengan anotasi tipe tambahan.
1function rangeCounter(min: number, max: number): () => number | string {
2 let count: number = min;
3
4 return function (): number | string {
5 if (count <= max) {
6 return count++;
7 } else {
8 return `Count has exceeded the maximum value: ${max}`;
9 }
10 };
11}
12
13const counter: () => number | string = rangeCounter(1, 5);
14
15console.log(counter()); // 1
16console.log(counter()); // 2
17console.log(counter()); // 3
18console.log(counter()); // 4
19console.log(counter()); // 5
20console.log(counter()); // "Count has exceeded the maximum value: 5"
- Fungsi
rangeCounter
mengembalikan fungsi yang mengembalikannumber
ataustring
. - Di dalam fungsi bagian dalam, jika
count
melebihimax
, ia akan mengembalikan pesan bertipestring
; jika tidak, akan mengembalikannumber
.
Perhatian saat Menggunakan Closure
Potensi Kebocoran Memori dari Closure
Closure dapat mempertahankan variabel dari lingkup eksternal, yang terkadang dapat menyebabkan kebocoran memori. Closure yang tidak diperlukan harus secara eksplisit dilepaskan dari memori.
1function createLeak(): () => void {
2 // Large array consuming significant memory
3 const largeArray: string[] = new Array(1000000).fill("leak");
4
5 // Closure capturing `largeArray`
6 return function (): void {
7 console.log(largeArray[0]); // Using the captured array
8 };
9}
10
11// Create a closure that holds a reference to the large array
12let leakyFunction = createLeak();
13
14// The large array is not released as `leakyFunction` still references it
15
16// When the object is no longer needed
17leakyFunction = null; // Explicitly remove the reference
- Dalam kode ini,
largeArray
yang dibuat di dalamcreateLeak
seharusnya dilepaskan ketika keluar dari lingkup, tetapi tidak demikian karena closure menangkaplargeArray
. SelamaleakyFunction
masih ada, memori yang tidak diperlukan ini akan terus dipertahankan. - Ketika sebuah objek atau variabel tidak lagi diperlukan, mengatur referensinya menjadi
null
memungkinkan garbage collector mendeteksi dan melepaskan memori tersebut.
Penyalahgunaan Closure di Dalam Loop
Ketika membuat closure di dalam sebuah loop, bisa terjadi masalah jika merujuk pada variabel yang sama. Contoh berikut menunjukkan kasus di mana variabel i
tidak berfungsi dengan benar.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Kode ini tidak menghasilkan hasil yang diinginkan karena i
merujuk pada nilai 3 di akhir loop. Untuk memperbaikinya, gunakan let
untuk memisahkan lingkup atau gunakan fungsi yang langsung dipanggil (IIFE).
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
Dengan menggunakan let
, lingkup dari i
dipisahkan untuk setiap iterasi loop, sehingga menghasilkan hasil yang diharapkan.
Ringkasan
Di TypeScript, closure dapat menghasilkan kode yang lebih aman dan dapat diprediksi dengan memanfaatkan sistem tipe. Penggunaan closure yang tepat memungkinkan enkapsulasi data dan desain fungsi higher-order yang fleksibel. Selain itu, harus berhati-hati terhadap manajemen memori dan referensi lingkup yang tidak diinginkan saat menggunakan closure.
Anda dapat mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Silakan periksa juga saluran YouTube kami.