Tartışma

JavaScript Scope ve Closure Mantığı

Başlatan DarkProtocol · 10 Tem 2026 19:35 · 16 Görüntülenme · 0 Yanıtlar
Konuyu Açan #0
JavaScript Scope ve Closure Mantığı

Merhaba arkadaşlar,

JavaScript dünyasında derinlemesine bir yolculuğa çıkmaya hazır mısınız? Bugün, dilin en temel ama bir o kadar da karmaşık görünen iki konsepti olan "Scope" (Kapsam) ve "Closure" (Kapanım) üzerine konuşacağız. Bu iki kavramı tam olarak anlamak, sadece yazdığınız kodun nasıl çalıştığını kavramanıza yardımcı olmakla kalmayacak, aynı zamanda daha temiz, daha güvenli ve daha performanslı uygulamalar geliştirmenizin de kapısını aralayacak. Birçoğumuz başlangıçta bu konuları yüzeysel geçebiliriz, ancak gerçek bir JavaScript ustası olmak istiyorsanız, bu yapı taşlarını sağlam bir şekilde oturtmak şart. Gelin, bu temel dinamikleri birlikte inceleyelim ve aklımızdaki soru işaretlerini giderelim...

Scope (Kapsam) Nedir?

Scope, basitçe ifade etmek gerekirse, bir değişkenin veya fonksiyonun erişilebilir olduğu "alan"ı tanımlar. JavaScript motoru, bir değişkeni ararken belirli kurallara göre hareket eder ve bu kurallar scope tarafından belirlenir. Bir değişkenin nerede tanımlandığı, onun nerede kullanılabileceğini doğrudan etkiler. Bu, kodunuzun öngörülebilirliğini artırır ve beklenmedik hataların önüne geçer. Kapsam mekanizması sayesinde, farklı kod parçaları arasında isim çakışmalarını önleyebilir, veri güvenliğini sağlayabiliriz.

JavaScript'te temel olarak üç farklı kapsam türünden bahsedebiliriz:

  • Global Scope (Küresel Kapsam): Bir değişkenin herhangi bir fonksiyon veya blok dışında tanımlandığı durumdur. Bu tür değişkenlere uygulamanızın her yerinden erişilebilir. Kolaylık gibi görünse de, global kapsamda çok fazla değişken tanımlamak, "global namespace pollution" (küresel isim alanı kirliliği) olarak bilinen bir soruna yol açabilir. Bu durum, farklı scriptlerin veya kütüphanelerin aynı isimde global değişkenler tanımlayarak birbirlerinin değerlerini ezmesine neden olabilir. Bu yüzden, global kapsam kullanımını minimumda tutmak her zaman iyi bir pratiktir.
    CODE
    123456789
    let globalDegisken = "Ben globalim!";
    
    function fonksiyonum() {
      console.log(globalDegisken); // Fonksiyon içinden erişilebilir
    }
    
    fonksiyonum(); // Çıktı: Ben globalim!
    console.log(globalDegisken);   // Çıktı: Ben globalim!
    


  • Function Scope (Fonksiyon Kapsamı): var anahtar kelimesiyle tanımlanan değişkenler, içinde bulundukları fonksiyonun kapsamına aittir. Bu, değişkenin yalnızca o fonksiyon içinde erişilebilir olduğu anlamına gelir. Fonksiyon dışından bu değişkenlere erişmeye çalışmak bir referans hatasına (ReferenceError) yol açar. Bu durum, özellikle büyük projelerde, değişkenlerin yanlışlıkla dışarıdan değiştirilmesini engelleyerek kodun güvenliğini artırır. Fonksiyonlar, kendi içlerinde tanımlanan değişkenler için adeta birer izole dünya yaratır diyebiliriz.
    CODE
    12345678
    function ornekFonksiyon() {
      var fonksiyonDegiskeni = "Sadece fonksiyon içindeyim.";
      console.log(fonksiyonDegiskeni); // Fonksiyon içinde erişilebilir
    }
    
    ornekFonksiyon(); // Çıktı: Sadece fonksiyon içindeyim.
    // console.log(fonksiyonDegiskeni); // Hata: ReferenceError: fonksiyonDegiskeni is not defined
    


  • Block Scope (Blok Kapsamı): ES6 ile hayatımıza giren let ve const anahtar kelimeleriyle tanımlanan değişkenler, içinde bulundukları süslü parantez bloklarının ({}) kapsamına aittir. Bu bloklar bir if ifadesi, bir for döngüsü veya sadece bağımsız bir kod bloğu olabilir. let ve const ile tanımlanan değişkenler, bu blokların dışından erişilemez. Bu özellik, var ile yaşanan bazı beklenmedik durumları (özellikle döngülerde) ortadan kaldırarak daha öngörülebilir ve hatasız kod yazmamızı sağlar. Modern JavaScript geliştirmenin vazgeçilmez bir parçasıdır.
    CODE
    12345678910111213141516
    if (true) {
      let blokDegiskeni = "Sadece blok içindeyim.";
      const sabitBlokDegiskeni = "Ben de sadece blok içindeyim.";
      console.log(blokDegiskeni);      // Çıktı: Sadece blok içindeyim.
      console.log(sabitBlokDegiskeni); // Çıktı: Ben de sadece blok içindeyim.
    }
    
    // console.log(blokDegiskeni);       // Hata: ReferenceError: blokDegiskeni is not defined
    // console.log(sabitBlokDegiskeni);  // Hata: ReferenceError: sabitBlokDegiskeni is not defined
    
    for (let i = 0; i < 3; i++) {
      // i sadece bu döngü bloğu içinde yaşar
      console.log(i);
    }
    // console.log(i); // Hata: ReferenceError: i is not defined
    

Peki, JavaScript bir değişkeni ararken nasıl bir yol izler? İşte burada "Scope Chain" (Kapsam Zinciri) devreye giriyor. Bir fonksiyon kendi içinde bir değişkene erişmeye çalıştığında, önce kendi lokal kapsamına bakar. Eğer orada bulamazsa, bir üst kapsamına (onu çevreleyen fonksiyona veya global kapsama) bakar ve bu işlemi değişkeni bulana veya global kapsama kadar gelip hala bulamayarak bir hata fırlatana kadar devam ettirir. Bu zincir, fonksiyonların nerede tanımlandığına (lexical environment) göre belirlenir, nerede çağrıldığına göre değil. Bu ayrım çok kritik, unutmayın.

Closure (Kapanım) Nedir?

Şimdi gelelim JavaScript'in en güçlü ve en çok yanlış anlaşılan özelliklerinden birine: Closure. Closure, bir fonksiyonun, tanımlandığı lexical environment'ı (sözcüksel ortamını) "hatırlaması" ve bu ortama erişebilmesi durumudur. Daha basit bir ifadeyle, bir iç fonksiyon, onu çevreleyen (dış) fonksiyonun değişkenlerine, dış fonksiyonun yürütülmesi tamamlanmış olsa bile erişmeye devam edebilir. Bu, dış fonksiyonun kapsamının, iç fonksiyon var olduğu sürece "canlı" kalması anlamına gelir.

Bu durum, özellikle fonksiyonların başka fonksiyonlar döndürdüğü veya bir değişkene referansın uzun süre saklanması gerektiği senaryolarda ortaya çıkar. Bir örnekle açıklayalım:

CODE
1234567891011121314
function disFonksiyon(disDegisken) {
  return function icFonksiyon(icDegisken) {
    console.log("Dış değişken:", disDegisken);
    console.log("İç değişken:", icDegisken);
  };
}

const benimKapanim = disFonksiyon("Merhaba Dünya"); // disFonksiyon çalıştı ve bitti
benimKapanim("JavaScript"); // icFonksiyon çalıştı ve disDegisken'e erişebildi

// Çıktı:
// Dış değişken: Merhaba Dünya
// İç değişken: JavaScript


Yukarıdaki örnekte, disFonksiyon çağrıldığında disDegisken değeri "Merhaba Dünya" olarak ayarlanır. disFonksiyon bir icFonksiyon döndürür ve yürütülmesi tamamlanır. Normalde disDegisken'in bellekten silinmesi beklenir. Ancak, benimKapanim değişkenine atanan icFonksiyon, hala disDegisken'e erişebilir! İşte bu, Closure'ın gücüdür. icFonksiyon, tanımlandığı ortamı (yani disFonksiyon'un kapsamını) "kapatır" ve o ortamdaki değişkenleri kendi içinde saklar.

Closure'ın Pratik Kullanım Alanları:

Closure'lar, JavaScript'te çok çeşitli ve güçlü tasarım kalıpları oluşturmak için kullanılır. İşte bunlardan bazıları:

[LIST]
  • Özel Değişkenler ve Veri Kapsülleme (Private Variables & Data Encapsulation): JavaScript'te doğrudan "private" anahtar kelimesi olmasa da (ES2022 ile sınıf içinde # öneki geldi ama bu fonksiyonlar için geçerli değil), closure'lar sayesinde dışarıdan erişilemeyen özel değişkenler oluşturabiliriz. Bu, nesne tabanlı programlamadaki kapsülleme ilkesini simüle etmemizi sağlar. Bir modülün iç durumunu korumak ve sadece belirli yöntemlerle erişilmesini sağlamak için idealdir.
    CODE
    1234567891011121314151617181920212223242526
    function sayacOlusturucu() {
      let sayac = 0; // Bu değişken dışarıdan doğrudan erişilemez
    
      return {
        arttir: function() {
          sayac++;
          console.log("Sayac:", sayac);
        },
        azalt: function() {
          sayac--;
          console.log("Sayac:", sayac);
        },
        degeriGor: function() {
          return sayac;
        }
      };
    }
    
    const benimSayacim = sayacOlusturucu();
    benimSayacim.arttir(); // Çıktı: Sayac: 1
    benimSayacim.arttir(); // Çıktı: Sayac: 2
    benimSayacim.azalt();  // Çıktı: Sayac: 1
    console.log("Şu anki değer:", benimSayacim.degeriGor()); // Çıktı: Şu anki değer: 1
    
    // console.log(benimSayacim.sayac); // undefined
    


  • Memoization (Önbellekleme): Bir fonksiyonun pahalı hesaplamalarının sonuçlarını önbelleğe almak için closure'lar kullanılabilir. Böylece aynı girdiyle tekrar çağrıldığında hesaplama yapmak yerine önbellekten sonucu döndürür. Bu, performans açısından ciddi kazançlar sağlayabilir.
    CODE
    12345678910111213141516171819202122232425262728
    function memoize(fn) {
      const cache = {}; // Önbellek closure içinde saklanır
      return function(...args) {
        const key = JSON.stringify(args);
        if (cache[key]) {
          console.log('Önbellekten çekiliyor...');
          return cache[key];
        } else {
          console.log('Hesaplanıyor...');
          const result = fn.apply(this, args);
          cache[key] = result;
          return result;
        }
      };
    }
    
    function pahaliIslem(num) {
      // Çok uzun süren bir hesaplama olduğunu varsayalım
      for (let i = 0; i < 10000000; i++) {}
      return num * 2;
    }
    
    const memoizeEdilmisIslem = memoize(pahaliIslem);
    
    console.log(memoizeEdilmisIslem(5)); // Hesaplanıyor... 10
    console.log(memoizeEdilmisIslem(5)); // Önbellekten çekiliyor... 10
    console.log(memoizeEdilmisIslem(10)); // Hesaplanıyor... 20
    


  • Olay İşleyicileri (Event Handlers): Tarayıcı ortamında olay işleyicileri genellikle closure'lardan faydalanır. Bir döngü içinde dinamik olarak olay işleyicileri atarken, her bir işleyicinin döngüdeki o anki değişkene erişmesini sağlamak için closure kullanılır. var ile bu durum sorun yaratırken, let ile blok kapsamı sayesinde daha doğal bir şekilde çözülür.
    [code]
    // Eski yöntem (var ile sorunlu)
    // for (var i = 1; i
  • Yanıt vermek için giriş yapmış olmalısınız.

    0 alıntı seçildi