JAVASCRIPT SCOPE CLOSURE ÖRNEKLEMEK

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; bu iki kavramı anlamadan modern JavaScript yazılamaz.
1. Scope nedir?
Bir değişkenin görünür olduğu kod alanı. Kavramların kesin tanımları için web teknolojileri dokümantasyonu ayrıntılı örnekler içerir. 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ız3. 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)2. var, let, const farkı
| Özellik | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (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.
3. 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.
4. 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()); // 3sayacOlustur 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.

5. 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 yok2. 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)); // 503. 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 korunur4. 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ür6. 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.
7. 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');
};8. 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();9. 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);
}
}
10. Tipik Hatalar
- var kullanmak. Modern JavaScript'te let/const tercih edilir; var bug riski yüksek
- for içinde var ile closure. Klasik soru; let kullanmak çözer
- Global scope kirletme. Modül yapısı kullanmadan global değişken eklemek
- Memory leak. Closure ile büyük objeleri tutmak
- this'in scope ile karıştırılması. this dinamik; scope statik
11. 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.
12. 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.
Sıkça Sorulan Sorular
Closure'ı pratik olarak nerede kullanırım?
En yaygın kullanım: private değişkenler (Module pattern), event handler'larda state, setTimeout/setInterval içinde değişken erişimi, currying ve higher-order functions, React useState gibi hooks'ların altyapısı. Modern JavaScript'in çoğu özelliği altta closure kullanır; bilerek yazmasanız da kütüphaneler closure ile çalışır.
var yerine neden let kullanmalıyım?
Üç ana sebep. Birincisi: block scope; daha güvenli ve öngörülebilir. var function scope'lu olduğu için beklenmedik davranışlar yaratır. İkincisi: redeclare engellenir; aynı isimle iki değişken tanımlamak hata verir, bug'ları önler. Üçüncüsü: TDZ (temporal dead zone) hoisting davranışını daha sıkı yapar. Modern JavaScript'te var artık tercih edilmez.
Closure memory leak'e neden olur mu?
Olabilir. Closure dış scope'taki tüm değişkenlere referans tutar; o değişkenler kullanılmasa bile bellekten silinmez. Özellikle DOM element'lerine veya büyük data structure'lara referans tutan closure'lar tehlikeli. Çözüm: gerçekten ihtiyaç olmayan değişkenleri closure dışında tutun; gerekirse null ataması ile manuel temizleyin.
Block scope ne işe yarar?
Değişkenleri sadece gerekli alanda tutmaya yarar. if, for, while gibi blokların içinde tanımlanan let/const değişkenler dışarıdan erişilemez. Bu kapsam küçültme bug'ları azaltır; global namespace temiz kalır. Function scope'a göre çok daha kısıtlayıcı ama güvenli.
JavaScript dinamik scope kullanır mı?
Hayır, lexical scope kullanır. Bir değişkenin nereden erişilebileceği kod yazılırken belirlenir; çalışma anında değil. Bash ve bazı eski diller dynamic scope kullanır (çağrılan yere göre değişken bakar). JavaScript'te lexical scope sayesinde kodu okurken davranışı tahmin etmek mümkün; bu büyük bir avantaj.
this neden scope'tan farklı?
this dinamik bir referans; çağrı bağlamına göre değişir. Aynı fonksiyon farklı şekillerde çağrıldığında farklı this değeri alır. Scope ise statik; fonksiyon tanımlandığı yer belirler. Arrow function'lar this'i 'lexical' yapar; tanımlandığı yerin this'ini alır. Bu, callback'lerde sık yaşanan 'this undefined' sorununu çözer.
Hoisting tam olarak ne yapar?
JavaScript yorumlayıcısı kod çalıştırmadan önce tüm tanımlamaları (var, function declaration) scope başına 'kaldırır'. var ile tanımlanan değişkenler undefined olarak hazır beklerken; let/const TDZ'de (henüz initialize edilmemiş) durur. Function declaration'lar tamamen hoisted; tanımlanmadan önce çağrılabilirler. Function expression (const fn = function(){}) sadece değişken hoist olur, fonksiyon değil.


