Funksiyalar və closure-lər
Dart-da parametrlərin üç formasi var və onları qarışdırmaq ən çox rast gəlinən sintaksis səhvidir:
- Positional (məcburi):
void log(String msg, int level) {}— sıra ilə ötürülür. - Optional positional — kvadrat mötərizə:
void log(String msg, [int level = 0]) {}— buraxıla bilər, default dəyər verilə bilər. - Named — fiqurlu mötərizə:
void log({required String msg, int level = 0}) {}— çağırışda ad yazılır:log(msg: 'salam').
Qaydalar: [] və {} eyni imzada birlikdə işlənə bilməz; null safety-dən sonra default dəyəri olmayan non-nullable named parametr mütləq required olmalıdır; default dəyər həmişə compile-time sabiti olmalıdır (const [] olar, [] olmaz — daha doğrusu, const konteksti tələb olunur).
| Forma | İmza | Çağırış | Nə vaxt |
|---|---|---|---|
| Positional | `f(int a, int b)` | `f(1, 2)` | 1-2 aydın arqument |
| Optional positional | `f(int a, [int b = 0])` | `f(1)` və ya `f(1, 2)` | Nadir; köhnə API-lərdə |
| Named | `f({required int a, int b = 0})` | `f(a: 1, b: 2)` | 3+ arqument, bool bayraqlar, konfiq obyektləri |
Funksiyalar birinci dərəcəli dəyərlərdir. Funksiyanı dəyişənə mənimsətmək, parametr kimi ötürmək və qaytarmaq olar. Funksiya tipini yazmağın iki yolu var:
- İnline:
void run(int Function(String) parse) {} typedefilə ad verərək:typedef Parser = int Function(String raw);
typedef uzun callback imzalarını oxunaqlı edir və eyni imza bir neçə yerdə təkrarlananda dəyişikliyi bir nöqtəyə yığır. Anonim funksiya ((x) => x * 2) və arrow sintaksisi (=>) yalnız tək ifadə üçün işləyir — => bloku deyil, ifadəni qaytarır.
Diqqət: Function (böyük hərflə, parametrsiz) faktiki olaraq "hər hansı funksiya" deməkdir və tip yoxlamasını itirir — həmişə imzanı tam yaz: bool Function(User).
Closure — funksiya plus onun yarandığı leksik mühit. Dart-da closure xarici dəyişəni dəyərinə görə deyil, dəyişənin özünə görə tutur: dəyişən sonra dəyişsə, closure yeni dəyəri görür.
Bu, klassik loop-capture sualının əsasıdır. Dart-da for (var i = 0; i < 3; i++) hər iterasiya üçün yeni `i` binding-i yaradır, ona görə toplanan closure-lər 0, 1, 2 çap edir — JavaScript-in var davranışından fərqli olaraq. Amma dövrdən kənarda elan olunmuş dəyişən tək binding-dir və bütün closure-lər son dəyəri görür:
for (var i = 0; ...)→ 0, 1, 2 (hər iterasiyada yeni binding)var i; for (i = 0; ...)→ 3, 3, 3 (tək binding)
Closure-lər həm də yaddaş saxlayır: callback obyekti tutursa, o obyekt GC oluna bilmir. Uzunömürlü listener-lərdə this-i tutan closure yaddaş sızmasının tipik səbəbidir.
Interview ipucu. Loop-capture sualı Dart-da tələ kimi verilir, çünki müsahiblərin çoxu JavaScript cavabını gözləyir. Güclü cavab: "Dart-da `for` dövrünün `var` ilə elan olunan sayğacı hər iterasiya üçün yeni binding alır, ona görə nəticə 0, 1, 2-dir. Əgər dəyişən dövrdən kənarda elan olunubsa, tək binding var və nəticə 3, 3, 3 olur." Sonra əlavə et ki, for-in dövründə də hər element yeni binding-dir.
İkinci tez-tez verilən sual: "Named parametrləri niyə positional-dan üstün tutursan?" — cavabda foo(true, false, true) kimi çağırışın oxunmazlığını, required-in compile-time təhlükəsizliyini və gələcəkdə parametr əlavə etməyin sıraya toxunmadan mümkün olduğunu vurğula.
📚 Mənbələr və sənədlər
- Funksiyalarrəsmidart.dev