↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)

3 dk okuma #dart
Dizi · 24/64 Dart Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Dart Nedir, Nerede Kullanılır, Neden Flutter Bu Dili Seçti
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Dart SDK, DartPad, Terminal ile Çalıştırma)
  3. Bölüm 1 — Konu 3: İlk Program (`main()`, `print()`, Dosya Yapısı)
  4. Bölüm 1 — Konu 4: Yorum Satırları, Temel Sözdizimi Kuralları
  5. Bölüm 2 — Konu 5: Değişken Tanımlama — `var`, `final`, `const` Farkı
  6. Bölüm 2 — Konu 6: Temel Tipler — `int`, `double`, `String`, `bool`
  7. Bölüm 2 — Konu 7: Tip Çıkarımı (Type Inference) ve Açık Tip Belirtme
  8. Bölüm 2 — Konu 8: `dynamic` ve `Object` — Ne Zaman Kullanılır, Ne Zaman Kaçınılır
  9. Bölüm 2 — Konu 9: Null Safety Temelleri (`?`, `!`, `late`'e Giriş)
  10. Bölüm 3 — Konu 10: Aritmetik, Atama, Karşılaştırma, Mantıksal Operatörler
  11. Bölüm 3 — Konu 11: `if / else if / else`
  12. Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)
  13. Bölüm 3 — Konu 13: Ternary Operatör, `??`, `??=`, `?.`
  14. Bölüm 3 — Konu 14: `for`, `while`, `do-while` Döngüleri
  15. Bölüm 4 — Konu 16: `List` — Oluşturma, Erişim, Temel Metodlar
  16. Bölüm 4 — Konu 17: `Set` — Benzersiz Eleman Mantığı
  17. Bölüm 4 — Konu 18: `Map` — Key-Value Yapılar
  18. Bölüm 4 — Konu 19: Koleksiyon Üzerinde `for-in`, `forEach`
  19. Bölüm 4 — Konu 20: Spread Operatörü (`...`, `...?`) ve Collection If/For
  20. Bölüm 5 — Konu 21: Fonksiyon Tanımlama, Parametreler (Positional, Named, Optional)
  21. Bölüm 5 — Konu 22: Varsayılan Parametre Değerleri (Derinlemesine)
  22. Bölüm 5 — Konu 23: Arrow Function (`=>`) Sözdizimi
  23. Bölüm 5 — Konu 24: Fonksiyonlar Birinci Sınıf Vatandaş — Değişkene Atama, Parametre Olarak Geçme
  24. Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)
  25. Bölüm 5 — Konu 26: Recursion (Özyineleme)
  26. Bölüm 6 — Konu 27: Class Tanımlama, Constructor (Varsayılan, Named, Factory)
  27. Bölüm 6 — Konu 28: Alanlar (Fields), Metodlar, `this` Kullanımı (Derinlemesine)
  28. Bölüm 6 — Konu 29: Initializer List, Constructor Kısayolları (Derinlemesine)
  29. Bölüm 6 — Konu 30: Getter / Setter
  30. Bölüm 6 — Konu 31: Statik Üyeler (`static`)
  31. Bölüm 7 — Konu 32: Kalıtım (`extends`), `super` Kullanımı
  32. Bölüm 7 — Konu 33: Metod Override Etme, `@override`
  33. Bölüm 7 — Konu 34: Soyut Sınıflar (`abstract class`)
  34. Bölüm 7 — Konu 35: Interface Mantığı (`implements`)
  35. Bölüm 7 — Konu 36: Mixin (`with`)
  36. Bölüm 7 — Konu 37: `enum` — Basit ve Gelişmiş (Metotlu Enum'lar)
  37. Bölüm 8 — Konu 38: `try / catch / finally`, `throw`
  38. Bölüm 8 — Konu 39: Özel Exception Sınıfları Yazma
  39. Bölüm 8 — Konu 40: Sound Null Safety Derinlemesine — `late`, `required`, Non-Nullable Varsayılan Davranış
  40. Bölüm 8 — Konu 41: `assert` ile Geliştirme Zamanı Kontrolleri
  41. Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
  42. Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)
  43. Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır
  44. Bölüm 10 — Konu 45: `Future` Kavramı, `async`/`await`
  45. Bölüm 10 — Konu 46: `Future.then`, Hata Yönetimi (`catchError`)
  46. Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
  47. Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
  48. Bölüm 11 — Konu 49: `map`, `where`, `reduce`, `fold`
  49. Bölüm 11 — Konu 50: `sort`, Custom Comparator ile Sıralama
  50. Bölüm 11 — Konu 51: Iterable Kavramı Derinlemesine (Lazy Evaluation)
  51. Bölüm 11 — Konu 52: Cascade Notasyonu (`..`)
  52. Bölüm 12 — Konu 53: `pubspec.yaml`, pub.dev'den Paket Ekleme
  53. Bölüm 12 — Konu 54: Kütüphane Oluşturma, `import`/`export`, `part`/`part of`
  54. Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
  55. Bölüm 12 — Konu 56: Extension Methods
  56. Bölüm 12 — Konu 57: Dart'ın Derleme Modelleri (JIT vs AOT) ve Bunun Flutter'a Etkisi
  57. Bölüm 13 — Konu 59: Dart 3 Pattern Matching — Records, Destructuring, Sealed Classes, Exhaustive Switch
  58. Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
  59. Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
  60. Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, `const` Nesnelerin Bellek Avantajı
  61. Bölüm 13 — Konu 63: Isolate'ler Arası Mesajlaşmanın Maliyeti ve `compute()`'un İç İşleyişi
  62. Bölüm 13 — Konu 64: FFI (Foreign Function Interface) — C Koduna Erişim
  63. Bölüm 13 — Konu 65: Sunucu Tarafında Dart — `dart:io`, `shelf` Paketiyle Basit Bir Backend/CLI Aracı Yazma
  64. Bölüm 13 — Konu 66: Derleyici & Analiz Araçları — `dart analyze`, Custom Lint Kuralları, `build_runner` Mimarisi
Dizinin sayfası →
İçindekiler 11 başlık
  1. Closure Nedir?
  2. Her Çağrı Kendi Closure'ını Oluşturur
  3. Closure'ın Pratik Kullanım Alanları
  4. 1. Özelleştirilmiş Fonksiyonlar Üretme
  5. 2. Callback Fonksiyonlarda Dış Değişkenlere Erişim
  6. 3. State (Durum) Saklama — Flutter'a Doğrudan Bağlantı
  7. Closure ile Değişken Yakalama — Dikkat Edilmesi Gereken Bir Nokta
  8. final/const ile Closure İlişkisi
  9. 🎯 Bu Dersten Çıkarılması Gerekenler
  10. 📝 Ödevler
  11. 🏁 Bölüm 5'in Beşinci Konusu Tamamlandı!

Önceki derste anonim fonksiyonları ve bir fonksiyonun başka bir fonksiyon döndürebildiğini görmüştük. Bu derste, o örnekte gizlice karşımıza çıkan ama üzerinde durmadığımız çok önemli bir kavramı derinlemesine işleyeceğiz: closure.

Closure Nedir?

Closure, bir fonksiyonun, tanımlandığı ortamdaki (scope) değişkenleri "hatırlaması" ve o değişkenlere erişebilmesi durumudur — fonksiyon çalıştırıldığı yerden çok uzakta olsa bile.

Bunu somut bir örnekle görelim:

dart
Function sayacOlustur() {
  int sayac = 0; // bu değişken, sayacOlustur'un LOKAL scope'unda

  int sayacArtir() {
    sayac++; // dıştaki 'sayac' değişkenine erişiyor
    return sayac;
  }

  return sayacArtir; // fonksiyonu döndürüyoruz (henüz çağırmadan)
}

void main() {
  var sayac1 = sayacOlustur();

  print(sayac1()); // 1
  print(sayac1()); // 2
  print(sayac1()); // 3 — her çağrıda sayac değeri KORUNUYOR ve artıyor!
}

Burada olağanüstü olan ne? Normalde, bir fonksiyon çalışmasını bitirdiğinde, o fonksiyonun içindeki lokal değişkenler (sayac gibi) bellekten silinir. Ama burada sayacOlustur() çalışmasını bitirmiş olmasına rağmen, döndürdüğü sayacArtir fonksiyonu hâlâ sayac değişkenine erişebiliyor ve onu güncelleyebiliyor. Bu, sayacArtir'ın bir closure oluşturduğu anlamına gelir — kendi tanımlandığı ortamın (sayac değişkenini içeren scope) bir "kapsülünü" (closure) yanında taşıyor.

Her Çağrı Kendi Closure'ını Oluşturur

dart
void main() {
  var sayac1 = sayacOlustur();
  var sayac2 = sayacOlustur(); // AYRI bir çağrı, AYRI bir closure

  print(sayac1()); // 1
  print(sayac1()); // 2
  print(sayac2()); // 1 — sayac2'nin kendi bağımsız sayacı var, sayac1'den etkilenmedi!
  print(sayac1()); // 3 — sayac1 kendi sayacına devam ediyor
}

Function sayacOlustur() {
  int sayac = 0;
  return () {
    sayac++;
    return sayac;
  };
}

Bu, closure'ların en önemli özelliğidir: her fonksiyon çağrısı, kendi bağımsız değişken kopyasını "kilitler" (closure oluşturur). sayac1 ve sayac2, aynı sayacOlustur fonksiyonundan üretilmiş olsalar da, birbirinden tamamen bağımsız çalışırlar.

Closure'ın Pratik Kullanım Alanları

1. Özelleştirilmiş Fonksiyonlar Üretme

dart
Function carpanOlustur(int carpan) {
  return (int sayi) => sayi * carpan;
}

void main() {
  var ikiyleCarpan = carpanOlustur(2);
  var onlaCarpan = carpanOlustur(10);

  print(ikiyleCarpan(5));  // 10
  print(onlaCarpan(5));    // 50
}

Burada carpan parametresi, döndürülen anonim fonksiyon tarafından "hatırlanıyor" — her üretilen fonksiyon, kendi carpan değerine sahip, birbirinden bağımsız bir closure.

2. Callback Fonksiyonlarda Dış Değişkenlere Erişim

dart
void main() {
  String kullaniciAdi = "Ahmet";

  List<int> sayilar = [1, 2, 3];

  sayilar.forEach((sayi) {
    print("$kullaniciAdi için sayı: $sayi"); // dıştaki kullaniciAdi'ye erişiyor
  });
}

Bu, aslında closure'ın en sık karşına çıkacağı kullanım şeklidir — anonim bir fonksiyon (burada forEach'e geçirilen), kendi dışındaki bir değişkene (kullaniciAdi) doğrudan erişebiliyor, parametre olarak almasına bile gerek kalmadan.

3. State (Durum) Saklama — Flutter'a Doğrudan Bağlantı

dart
Function butonSayaci() {
  int tiklamaSayisi = 0;
  return () {
    tiklamaSayisi++;
    print("Tıklama sayısı: $tiklamaSayisi");
  };
}

void main() {
  var buton1Tikla = butonSayaci();
  buton1Tikla(); // Tıklama sayısı: 1
  buton1Tikla(); // Tıklama sayısı: 2
  buton1Tikla(); // Tıklama sayısı: 3
}

Bu kalıp, ileride Flutter'da onPressed: () { setState(() { sayac++; }); } gibi yapılar yazarken karşına çıkacak mantığın temelidir — bir buton her tıklandığında çalışacak fonksiyon, kendi dışındaki bir durumu (state) "hatırlayıp" güncelleyebiliyor.

Closure ile Değişken Yakalama — Dikkat Edilmesi Gereken Bir Nokta

dart
void main() {
  List<Function> fonksiyonlar = [];

  for (int i = 0; i < 3; i++) {
    fonksiyonlar.add(() => print(i));
  }

  for (var f in fonksiyonlar) {
    f();
  }
  // Çıktı: 0, 1, 2
}

Dart'ta her for iterasyonu, i için yeni bir kopya oluşturur (bu davranış dilden dile değişebilir — örneğin bazı eski JavaScript sürümlerinde var ile bu farklı çalışırdı, hepsi son değeri yazardı). Dart'ta bu doğru ve beklenen şekilde çalışır: her closure, oluşturulduğu anki i değerini doğru şekilde yakalar.

final/const ile Closure İlişkisi

Bölüm 2'de öğrendiğimiz final/const kavramları, closure'larla birlikte kullanıldığında da tutarlı davranır — bir closure, dış scope'taki bir final değişkenin değerini "yakaladığında", o değer değişmeyeceği için closure'ın davranışı da öngörülebilir olur. var ile tanımlanan ve closure içinde güncellenen değişkenlerde ise (yukarıdaki sayaç örneklerinde gördüğümüz gibi) değer gerçekten değişebilir — bu, closure'ın "canlı bir referans" tuttuğunu, "o anki değerin bir kopyasını" tutmadığını gösterir.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Closure, bir fonksiyonun tanımlandığı ortamdaki değişkenlere, o ortam "kapanmış" (fonksiyon çalışmasını bitirmiş) olsa bile erişebilmesidir.
  • Her fonksiyon çağrısı kendi bağımsız closure'ını oluşturur — farklı çağrılar birbirinin durumunu etkilemez.
  • Closure'lar, özelleştirilmiş fonksiyonlar üretmek, callback'lerde dış değişkenlere erişmek ve durum (state) saklamak için kullanılır.
  • Dart'ta for döngülerinde her iterasyon kendi değişken kopyasını doğru şekilde closure'a yakalar.
  • Bu kavram, ileride Flutter'da state yönetimi ve callback fonksiyonları anlarken doğrudan işine yarayacak.

📝 Ödevler

  • [ ] sayacOlustur benzeri bir fonksiyon yaz, iki farklı sayaç oluşturup birbirinden bağımsız çalıştıklarını kanıtla.
  • [ ] Bir "çarpan üretici" fonksiyon yaz (carpanOlustur gibi), 3 farklı çarpanla 3 farklı fonksiyon üret ve hepsini test et.
  • [ ] Bir listeye closure içeren 5 farklı anonim fonksiyon ekleyen bir döngü yaz (yukarıdaki örneğe benzer), hepsini çalıştırıp doğru değerleri yakaladıklarını doğrula.
  • [ ] Dışarıdaki bir listeye, her çağrıda yeni bir eleman ekleyen bir closure fonksiyonu yaz (örn. logEkle fonksiyonu, çağrıldıkça dış bir listeye zaman damgası eklesin).

🏁 Bölüm 5'in Beşinci Konusu Tamamlandı!

Fonksiyonların temel yapısından, parametrelere, arrow function'a, birinci sınıf vatandaş olmalarına ve closure'lara kadar geniş bir yolculuk yaptık. Bölüm 5'in son konusu olan recursion (özyineleme) ile bu bölümü tamamlayacağız.

Sıradaki konu: Bölüm 5 — Konu 26: Recursion (Özyineleme)