Yazılarımız

OfisData

JAVASCRIPT SCOPE CLOSURE ÖRNEKLEMEK

Sarı JavaScript logosu yanında iç içe geçmiş outer ve inner scope kutuları ile zincir sembolü scope chain hiyerarşisi

JavaScript mülakatlarında en sık sorulan iki kavram: scope ve closure. Bu iki kavramı anlamadan modern JavaScript yazılamaz; modüller, async kod, framework'lerin çoğu altta bu mekanizmaları kullanır. Closure özellikle React hooks gibi yapıların temelinde durur; closure'ı bilmeden useState'in nasıl çalıştığı anlaşılmaz.

Scope basitçe "değişken hangi alanda görünür" sorusunu cevaplar. Closure ise "bir fonksiyon dışındaki değişkenlere nasıl erişir" sorusunu. İkisi birlikte JavaScript'in en güçlü ama en kafa karıştırıcı özelliklerinden; kavramların kesin tanımları için web teknolojileri dokümantasyonu ayrıntılı örnekler içerir.

Closure'ı anlamanın ön koşulu scope'u anlamaktır: önce scope türleri, sonra zincirin nasıl tırmandığı (scope chain), en sonda da fonksiyonun doğduğu ortamı hatırlaması — yani closure'ın kendisi. Pratik örnekler bu sıranın üzerine geldiğinde mekanizma ezber olmaktan çıkar.

Scope nedir?

Bir değişkenin görünür olduğu kod alanı. JavaScript'te üç scope türü var:

1. Global scope

Tüm dosyada görünür; en üst seviye.

const isim = 'Ahmet'; // global scope

function selamla() {
  console.log(isim); // erişebilir
}

2. Function scope

Fonksiyon içinde tanımlanan değişkenler sadece o fonksiyon içinde görünür.

function hesapla() {
  const sonuc = 42; // function scope
  console.log(sonuc); // OK
}

console.log(sonuc); // HATA: sonuc tanımsız

3. Block scope

let ve const ile tanımlanan değişkenler süslü parantez { } içinde görünür.

if (true) {
  let x = 10; // block scope
}

console.log(x); // HATA

// var farklı; function scoped
if (true) {
  var y = 10; // function veya global scope
}

console.log(y); // 10 (erişilebilir)

var, let, const farkı

Özellikvarletconst
ScopeFunctionBlockBlock
HoistingYes (undefined)Yes (TDZ)Yes (TDZ)
Redeclareİzin varİzin yokİzin yok
Reassignİzin varİzin varİzin yok

Modern pratik: const varsayılan; reassign gerekiyorsa let; var kullanma.

Scope chain

İç içe fonksiyonlarda değişken arama: en içten dışa doğru.

const a = 1; // global

function disFonksiyon() {
  const b = 2; // disFonksiyon scope

  function icFonksiyon() {
    const c = 3; // icFonksiyon scope
    console.log(a, b, c); // 1, 2, 3
    // a global'dan, b dış fonksiyondan, c kendi scope'tan
  }

  icFonksiyon();
}

icFonksiyon a'yı doğrudan bulamaz; scope chain ile yukarı çıkıp önce disFonksiyon'da bakar, sonra global'a bakar. Bulduğu ilk eşleşmeyi alır.

Closure nedir?

Bir fonksiyonun, kendisi tanımlandığı scope'taki değişkenlere erişebilmesi. Fonksiyon dışarı çıkarılsa bile o değişkenlere erişim devam eder.

function sayacOlustur() {
  let sayac = 0;

  return function() {
    sayac++;
    return sayac;
  };
}

const arttir = sayacOlustur();
console.log(arttir()); // 1
console.log(arttir()); // 2
console.log(arttir()); // 3

sayacOlustur fonksiyonu bittikten sonra bile dönen fonksiyon "sayac" değişkenine erişebiliyor. Bu closure'dur; dış fonksiyonun scope'unu "kapatıp" içeriye getiriyor.

VS Code dark theme editörde sayacOlustur closure fonksiyonu kodu syntax highlight ile dış değişken sayac kapatma örneği

Closure kullanım örnekleri

1. Private değişkenler

JavaScript'te native private değişken yoktu (modern class # syntax'ı hariç); closure ile simüle edilir.

function bankaHesabi(baslangic) {
  let bakiye = baslangic; // dışarıdan görünmez

  return {
    yatir: (miktar) => { bakiye += miktar; },
    cek: (miktar) => {
      if (miktar > bakiye) return 'Yetersiz';
      bakiye -= miktar;
      return bakiye;
    },
    sorgu: () => bakiye
  };
}

const hesap = bankaHesabi(1000);
hesap.yatir(500);
console.log(hesap.sorgu()); // 1500
console.log(hesap.bakiye); // undefined - dışarıdan erişim yok

2. Currying

Bir fonksiyonu parça parça çağırma; her aşamada bir argüman alıp yeni fonksiyon döner.

function carpan(x) {
  return function(y) {
    return x * y;
  };
}

const ikiyleCarp = carpan(2);
const beslyeCarp = carpan(5);

console.log(ikiyleCarp(10)); // 20
console.log(beslyeCarp(10)); // 50

3. Event handler'larda state

function butonHazirla() {
  let tiklamaSayisi = 0;

  document.querySelector('button').addEventListener('click', () => {
    tiklamaSayisi++;
    console.log(`Tıklama: ${tiklamaSayisi}`);
  });
}

butonHazirla();
// Buton tıklandığında tiklamaSayisi closure ile korunur

4. setTimeout closure

function gecikmeliMesaj(mesaj, ms) {
  setTimeout(() => {
    console.log(mesaj); // closure ile mesaj'a erişir
  }, ms);
}

gecikmeliMesaj('Merhaba', 1000);
// Fonksiyon biter ama setTimeout callback'i mesaj'ı görür

Closure tuzakları

1. for döngüsünde var ile closure sorunu

// Yanlış: var function-scoped
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Çıktı: 3, 3, 3 (beklenen 0, 1, 2 değil)
// Sebep: tüm callback'ler aynı i'yi paylaşır

// Doğru: let block-scoped
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Çıktı: 0, 1, 2 (her iterasyonda yeni i)

2. Memory leak riski

Closure dış scope'a referans tutar; o scope'taki tüm değişkenler bellekte kalır. Büyük objeleri yanlış closure'a sokmak memory leak yaratır.

function bigDataIslem() {
  const buyukVeri = new Array(1000000).fill('x');

  return function() {
    console.log('İşlem yapıldı');
    // buyukVeri kullanılmıyor ama closure ile bellekte tutulur
  };
}

const handler = bigDataIslem();
// buyukVeri hâlâ bellekte; garbage collect olmaz

Çözüm: ihtiyaç olmayan değişkenleri closure dışında tut.

Hoisting

JavaScript değişken ve fonksiyon tanımlamalarını scope başına "kaldırır". var ile tanımlananlar undefined ile; let/const ise "temporal dead zone" (TDZ) ile.

console.log(a); // undefined (var hoisted)
var a = 5;

console.log(b); // HATA: TDZ
let b = 10;

selamla(); // OK (function declaration hoisted)
function selamla() {
  console.log('Merhaba');
}

selamla2(); // HATA: function expression hoisted ama undefined
const selamla2 = function() {
  console.log('Merhaba');
};

Lexical vs dynamic scope

JavaScript lexical scope kullanır: bir değişkenin nereden erişilebileceği kod yazılırken belirlenir; çalışma anında değil.

const isim = 'Global';

function selamla() {
  console.log(isim); // 'Global' (lexical)
}

function farkliScope() {
  const isim = 'Lokal';
  selamla(); // hâlâ 'Global' yazar
  // Çünkü selamla tanımlandığı yerdeki isim'i görür
}

farkliScope();

this ve scope ilişkisi

this dinamik; çağrıldığı bağlama göre değişir. Arrow function'lar this'i kapatır (closure benzeri).

class Sayac {
  constructor() {
    this.deger = 0;
  }

  baslat() {
    // setInterval callback'i farklı this'e sahip
    setInterval(function() {
      this.deger++; // HATA: this undefined
    }, 1000);

    // Arrow function this'i kapatır
    setInterval(() => {
      this.deger++; // OK: outer this kullanılır
    }, 1000);
  }
}
Lexical scope chain diyagramında global block dış fonksiyon iç fonksiyon iç içe kutular ve yukarı yönlü değişken arama okları

Tipik Hatalar

  1. var kullanmak. Modern JavaScript'te let/const tercih edilir; var bug riski yüksek
  2. for içinde var ile closure. Klasik soru; let kullanmak çözer
  3. Global scope kirletme. Modül yapısı kullanmadan global değişken eklemek
  4. Memory leak. Closure ile büyük objeleri tutmak
  5. this'in scope ile karıştırılması. this dinamik; scope statik

Konu Üzerinde Derinleşmek

Scope ve closure modern JavaScript'in temel taşları. Konuya hâkim olmak için JavaScript eğitimi programları bu kavramları derinlemesine işler.

Kısa Özet

JavaScript'te scope değişken görünürlüğünü; closure fonksiyon ile değişkenlerin "kapatılmasını" yönetir. Block scope (let/const), function scope (var), global scope üç ana türü. Closure private değişkenler, currying, event handler state'i gibi pratik kullanımlar için temeldir. var yerine let/const, for döngüsünde let, arrow function ile this kapatma modern pratiklerdir. Closure memory leak'e dikkat; gereksiz büyük objeleri kapsam dışında tutun.

 Vimaj