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
, iç fonksiyon olaninnerFunction
'ı döndürür.innerFunction
, dış fonksiyonun değişkeni olanouterVariable
'ın değerini gösterir.
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
türünde bir değer döndüren 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. Burada, üst düzey bir fonksiyon, başka bir fonksiyonu argüman olarak alan veya sonuç olarak bir fonksiyon döndüren bir fonksiyondur. 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
, aldığı sayıyı çarpmak için bir fonksiyon oluşturan bir üst düzey fonksiyondur.- İçteki çarpma fonksiyonu da bir sayı alır ve çarpmanın sonucunu bir sayı olarak döndürür.
TypeScript'te Closure Kullanımı Örneği
Aralık tabanlı bir sayacı closure olarak uygulamaya yönelik bir örneğe de bakalım.
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, var
ile tanımlanan i
değişkeninin doğru şekilde davranmadığı 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.