TypeScript'te Closure
Bu makalede, TypeScript'teki closure kavramını açıklayacağız.
YouTube Video
TypeScript'te Closure
Closure Nedir?
Closure, bir fonksiyonun tanımlandığı kapsamı (ortamı) referans olarak tutma yeteneğidir; bu fonksiyon kapsam dışında çağrılsa bile bu ortamı korumasına olanak sağlar. Aşağıda, closure kavramı tip açıklamalarıyla birlikte açıklanacaktır.
Basitçe söylemek gerekirse, closure bir fonksiyon ile fonksiyonun tanımlandığı değişken ortamını birleştirir ve fonksiyon çağrıldığında bu ortama erişim sağlar.
Closure Mekanizmasının Temelleri
TypeScript'te, bir fonksiyon başka bir fonksiyon içinde tanımlandığında, içteki fonksiyonun dıştaki fonksiyonun değişkenlerine erişebildiği görülür. Aşağıda tip açıklamalarıyla birlikte temel bir closure örneği verilmiştir.
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"
outerFunction
fonksiyonunun dönüş tipi() => void
'dir, yani bu fonksiyon başka bir fonksiyon döndürür.innerFunction
'ın tipi açıkçavoid
olarak belirlenmiştir; yani herhangi bir dönüş değeri yoktur.
Closure Kullanım Alanları ve Avantajları
Veri Kapsülleme
Aşağıda veri kapsülleme için closure'ın tip açıklamalarıyla birlikte bir örneği verilmiştir.
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
createCounter
fonksiyonu() => number
tipinde bir fonksiyon döndürür.count
değişkeninumber
tipiyle tanımlanmıştır ve closure içinde işlenir.
Üst Düzey Fonksiyonlar (Higher-Order Functions)
Closure'lar, üst düzey fonksiyonlar oluştururken oldukça faydalıdır. Aşağıda net tip açıklamalarıyla birlikte bir üst düzey fonksiyon örneği verilmiştir.
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
,number
tipinde bir argüman alır ve(value: number) => number
tipinde bir fonksiyon döndürür.- İçteki fonksiyon da
value
parametresininumber
tipinde alır ve sonucunumber
tipinde döndürür.
TypeScript'te Closure Kullanımı Örneği
Tip açıklamalarıyla birlikte closure şeklinde aralıklı bir sayaç uygulaması gerçekleştirin.
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"
rangeCounter
fonksiyonu, birnumber
ya dastring
döndüren bir fonksiyon döndürür.- İçteki fonksiyonda,
count
değerimax
'ı aşarsa birstring
tipinde mesaj döner; aksi haldenumber
değer döndürür.
Closure Kullanırken Dikkat Edilmesi Gerekenler
Closure Kaynaklı Olası Bellek Sızıntıları
Closure'lar dış kapsamdan değişkenleri tutabilir ve bu durum bazen bellek sızıntılarına sebep olabilir. Gereksiz closure'ların bellekte kalmasını önlemek için açıkça serbest bırakılması gerekir.
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
- Bu kodda,
createLeak
fonksiyonu içinde oluşturulanlargeArray
değişkeni kapsam dışına çıktığında serbest bırakılması gerekir, ancak closurelargeArray
'ı tuttuğu için bu gerçekleşmez.leakyFunction
var olduğu sürece, bu gereksiz bellek tutulmaya devam edecektir. - Bir nesne veya değişken artık gerekmediğinde, referansını
null
yaparsanız çöp toplayıcı (garbage collector) bunu algılayıp belleği serbest bırakır.
Döngülerde Closure'ın Yanlış Kullanımı
Bir döngüde closure oluşturulurken, aynı değişkene referans verme konusunda sorunlar yaşanabilir. Aşağıdaki örnek, i
değişkeninin düzgün çalışmadığı bir durumu göstermektedir.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Bu kod, istenen sonucu vermez, çünkü döngünün sonunda i
değişkeni 3 değerini alır. Bunu düzeltmek için ya let
ile ayrı bir kapsam oluşturulmalı ya da hemen çağrılan fonksiyon kullanılmalıdır.
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
let
kullanarak, her döngü yinelemesinde i
için ayrı bir kapsam oluşturulur ve beklenen sonuç elde edilir.
Özet
TypeScript'te closure'lar, tip sistemi sayesinde daha güvenli ve öngörülebilir kodlar yazılmasını sağlar. Closure'ların doğru kullanımı veri kapsülleme ve üst düzey fonksiyonların esnek tasarımını mümkün kılar. Ayrıca, closure kullanırken bellek yönetimine ve istenmeyen kapsam referanslarına dikkat edilmelidir.
Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.