Closure di TypeScript

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 sebagai void, 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 tipe number 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 bertipe number dan mengembalikan fungsi bertipe (value: number) => number.
  • Fungsi bagian dalam juga menerima value bertipe number dan mengembalikan hasil bertipe number.

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 mengembalikan number atau string.
  • Di dalam fungsi bagian dalam, jika count melebihi max, ia akan mengembalikan pesan bertipe string; jika tidak, akan mengembalikan number.

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 dalam createLeak seharusnya dilepaskan ketika keluar dari lingkup, tetapi tidak demikian karena closure menangkap largeArray. Selama leakyFunction 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.

YouTube Video