Bu ders için video bulunmamaktadır.

Bu derse başlamak veya ilerlemenizi kaydetmek için lütfen giriş yapın veya kayıt olun.

Ders İçeriği

Giriş

JavaScript'te değişkenlerin ve fonksiyonların nerede erişilebilir olduğunu belirleyen kurallara kapsam (scope) denir. Kapsam, kodumuzun daha organize, güvenli ve hatasız olmasını sağlar. Değişkenlerin yanlışlıkla üzerine yazılmasını önler ve kodun farklı bölümlerinin birbirini etkilemesini sınırlar.

Closure ise, JavaScript'in güçlü ve bazen kafa karıştırıcı olabilen bir özelliğidir. Bir fonksiyonun, tanımlandığı kapsamdaki değişkenlere erişebilme yeteneğidir, fonksiyon başka bir kapsamda çalıştırılsa bile. Closure'lar, özel değişkenler oluşturmak, veri gizlemek ve daha gelişmiş programlama desenleri uygulamak için kullanılır.

Bu derste, JavaScript'teki farklı kapsam türlerini (global, fonksiyonel, blok) ve değişkenlerin bu kapsamlarda nasıl davrandığını öğreneceğiz. Ayrıca, closure kavramını, nasıl çalıştığını ve pratik kullanım alanlarını detaylı bir şekilde inceleyeceğiz.

Kapsam (Scope) Nedir?

Kapsam, kodunuzun belirli bir bölümünde hangi değişkenlere ve fonksiyonlara erişilebileceğini belirler. Bir değişken veya fonksiyon tanımladığınızda, o sadece belirli bir kapsam içinde geçerlidir.

JavaScript'te temel olarak üç tür kapsam vardır:

  • Global Kapsam (Global Scope): Herhangi bir fonksiyonun veya bloğun dışında tanımlanan değişkenler ve fonksiyonlar global kapsama aittir. Global kapsamdaki öğelere kodun her yerinden erişilebilir.
  • Fonksiyon Kapsamı (Function Scope): Bir fonksiyon içinde var anahtar kelimesiyle tanımlanan değişkenler, sadece o fonksiyon içinde erişilebilirdir.
  • Blok Kapsamı (Block Scope): let ve const anahtar kelimeleriyle (ES6 ile geldi) bir blok ({} süslü parantezler arası, örneğin ifforwhile blokları) içinde tanımlanan değişkenler, sadece o blok içinde erişilebilirdir.

Global Kapsam

Herhangi bir fonksiyon veya blok dışında tanımlanan değişkenler global kapsama aittir. Bu değişkenlere kodun herhangi bir yerinden erişilebilir.

global_kapsam.js
// Global kapsamda tanımlanan değişken
let globalDegisken = "Ben globalim!";

function birFonksiyon() {
    console.log("Fonksiyon içinden:", globalDegisken); // Erişilebilir
}

birFonksiyon();

console.log("Fonksiyon dışından:", globalDegisken); // Erişilebilir

// Çıktı:
// Fonksiyon içinden: Ben globalim!
// Fonksiyon dışından: Ben globalim!

Uyarı: Global değişkenleri aşırı kullanmaktan kaçının. Kodun farklı bölümlerinden erişilebilir olmaları, beklenmedik değişikliklere ve hatalara yol açabilir. Mümkün olduğunca değişkenleri yerel kapsamlarda (fonksiyon veya blok) tanımlamaya çalışın.

Tarayıcı ortamında, global kapsam genellikle window nesnesine karşılık gelir. Yani, global olarak tanımlanan bir değişken, window nesnesinin bir özelliği olur.

global_window.js
var globalVar = 10; // var ile tanımlanan global değişken window'a eklenir
let globalLet = 20; // let ile tanımlanan global değişken window'a eklenmez
const globalConst = 30; // const ile tanımlanan global değişken window'a eklenmez

console.log(window.globalVar); // 10 (Tarayıcı konsolunda çalıştırın)
console.log(window.globalLet); // undefined
console.log(window.globalConst); // undefined

Fonksiyon Kapsamı

Bir fonksiyon içinde var anahtar kelimesiyle tanımlanan değişkenler, fonksiyon kapsamına sahiptir. Bu değişkenlere sadece tanımlandıkları fonksiyon içinden erişilebilir.

fonksiyon_kapsami.js
function testFonksiyonKapsami() {
    var fonksiyonDegiskeni = "Ben fonksiyon kapsamındayım";
    console.log("Fonksiyon içinden:", fonksiyonDegiskeni); // Erişilebilir
}

testFonksiyonKapsami();

// console.log("Fonksiyon dışından:", fonksiyonDegiskeni); // Hata! fonksiyonDegiskeni is not defined

Fonksiyonlar içinde tanımlanan fonksiyonlar da (iç içe fonksiyonlar) dıştaki fonksiyonun kapsamındaki değişkenlere erişebilir.

ic_ice_fonksiyon_kapsami.js
function disFonksiyon() {
    var disDegisken = "Dış değişken";

    function icFonksiyon() {
        var icDegisken = "İç değişken";
        console.log(disDegisken); // Dış değişkene erişilebilir
        console.log(icDegisken);
    }

    icFonksiyon();
    // console.log(icDegisken); // Hata! icDegisken dışarıdan erişilemez
}

disFonksiyon();
// Çıktı:
// Dış değişken
// İç değişken

Blok Kapsamı

ES6 ile gelen let ve const anahtar kelimeleri, blok kapsamı kavramını JavaScript'e getirmiştir. Bir blok ({} süslü parantezler arası) içinde let veya const ile tanımlanan değişkenler, sadece o blok içinde geçerlidir.

blok_kapsami.js
if (true) {
    let blokDegiskeniLet = "Ben let ile bloktayım";
    const blokDegiskeniConst = "Ben const ile bloktayım";
    var blokDegiskeniVar = "Ben var ile bloktayım"; // var blok kapsamına sahip değildir!
    console.log(blokDegiskeniLet); // Erişilebilir
    console.log(blokDegiskeniConst); // Erişilebilir
    console.log(blokDegiskeniVar); // Erişilebilir
}

// console.log(blokDegiskeniLet); // Hata! blokDegiskeniLet is not defined
// console.log(blokDegiskeniConst); // Hata! blokDegiskeniConst is not defined
console.log(blokDegiskeniVar); // Erişilebilir! (var fonksiyon kapsamlıdır)

for (let i = 0; i < 3; i++) {
    console.log("Döngü içi i:", i);
}
// console.log("Döngü dışı i:", i); // Hata! i is not defined (let blok kapsamlı)

for (var j = 0; j < 3; j++) {
    console.log("Döngü içi j:", j);
}
console.log("Döngü dışı j:", j); // 3 (var fonksiyon kapsamlı olduğu için erişilebilir)

İpucu: Modern JavaScript'te, değişken tanımlamak için genellikle var yerine let ve const kullanılması önerilir. Blok kapsamı, değişkenlerin etki alanını daha iyi kontrol etmenizi sağlar ve beklenmedik hataları önlemeye yardımcı olur.

Kapsam Zinciri (Scope Chain)

JavaScript motoru bir değişkene erişmeye çalıştığında, önce mevcut kapsamda (örneğin, içinde bulunduğu fonksiyon veya blok) arar. Eğer değişken orada bulunamazsa, bir üst kapsama (örneğin, dıştaki fonksiyon veya global kapsam) bakar. Bu arama işlemi, değişken bulunana veya global kapsama ulaşılana kadar devam eder. Bu kapsamlardan oluşan hiyerarşiye kapsam zinciri denir.

kapsam_zinciri.js
let globalVar = "Global";

function dis() {
    let disVar = "Dış";

    function ic() {
        let icVar = "İç";
        console.log(icVar); // Mevcut kapsamda bulundu: "İç"
        console.log(disVar); // Bir üst kapsamda (dis) bulundu: "Dış"
        console.log(globalVar); // İki üst kapsamda (global) bulundu: "Global"
    }

    ic();
}

dis();

Closure Nedir?

Closure, bir fonksiyonun, tanımlandığı andaki kapsam zincirine (lexical scope) erişebilme yeteneğidir. Başka bir deyişle, bir fonksiyon, kendi kapsamı dışındaki (örneğin, onu içeren dış fonksiyonun kapsamındaki) değişkenlere erişebilir ve bu değişkenleri "hatırlayabilir", fonksiyon başka bir yerde çağrılsa bile.

Closure'lar, bir fonksiyonun başka bir fonksiyon tarafından döndürülmesiyle oluşur. İçteki fonksiyon, dıştaki fonksiyonun değişkenlerine erişimi korur.

closure_temel.js
function disFonksiyon() {
    let mesaj = "Merhaba Closure!";

    // İç fonksiyon, dış fonksiyonun 'mesaj' değişkenine erişir
    function icFonksiyon() {
        console.log(mesaj);
    }

    return icFonksiyon; // İç fonksiyonu döndür
}

// disFonksiyon çağrıldığında, icFonksiyon'u döndürür
// closureOlustur artık icFonksiyon'a referans verir
let closureOlustur = disFonksiyon(); 

// closureOlustur (yani icFonksiyon) çağrıldığında,
// disFonksiyon'un kapsamındaki 'mesaj' değişkenini hatırlar ve kullanır.
disFonksiyon çalışması bitmiş olsa bile!
closureOlustur(); // Çıktı: "Merhaba Closure!"

Yukarıdaki örnekte, disFonksiyon çalışıp bittikten sonra bile, closureOlustur değişkenine atanan icFonksiyondisFonksiyon'un kapsamındaki mesaj değişkenine erişmeye devam eder. İşte bu duruma closure denir.

Closure Kullanım Alanları

Closure'lar, JavaScript'te birçok güçlü desenin temelini oluşturur:

1. Veri Gizleme ve Özel Değişkenler (Data Encapsulation)

Closure kullanarak, bir fonksiyonun içindeki değişkenleri dışarıdan erişilemez hale getirebilir (özel değişkenler) ve bu değişkenlere sadece belirli metodlar aracılığıyla erişilmesini sağlayabilirsiniz.

closure_veri_gizleme.js
function sayacOlustur() {
    let sayac = 0; // Bu değişken dışarıdan erişilemez (özel)

    return {
        artir: function() {
            sayac++;
        },
        azalt: function() {
            sayac--;
        },
        deger: function() {
            return sayac;
        }
    };
}

let benimSayacim = sayacOlustur();

benimSayacim.artir();
benimSayacim.artir();
console.log(benimSayacim.deger()); // Çıktı: 2

benimSayacim.azalt();
console.log(benimSayacim.deger()); // Çıktı: 1

// console.log(benimSayacim.sayac); // Hata! 'sayac' değişkenine doğrudan erişilemez

2. Fonksiyon Fabrikaları (Function Factories)

Closure kullanarak, belirli parametrelerle yapılandırılmış yeni fonksiyonlar üreten fonksiyonlar (fonksiyon fabrikaları) oluşturabilirsiniz.

closure_fonksiyon_fabrikasi.js
function carpaniOlustur(carpan) {
    // Bu fonksiyon, aldığı sayıyı 'carpan' ile çarpan yeni bir fonksiyon döndürür
    return function(sayi) {
        return sayi * carpan;
    };
}

let ikiyleCarp = carpaniOlustur(2);
let ucleyCarp = carpaniOlustur(3);

console.log(ikiyleCarp(5)); // Çıktı: 10
console.log(ucleyCarp(5)); // Çıktı: 15
console.log(ikiyleCarp(10)); // Çıktı: 20

3. Callback Fonksiyonları ve Asenkron İşlemler

Closure'lar, callback fonksiyonlarında ve asenkron işlemlerde (örneğin, setTimeout, olay dinleyicileri) sıklıkla kullanılır. Callback fonksiyonunun, çağrıldığı andaki değil, tanımlandığı andaki değişkenlere erişmesi gerektiğinde closure devreye girer.

closure_callback.js
function bekleVeYazdir(mesaj, sure) {
    setTimeout(function() {
        // Bu callback fonksiyonu, dıştaki 'mesaj' değişkenini hatırlar (closure)
        console.log(mesaj);
    }, sure);
}

bekleVeYazdir("Merhaba 2 saniye sonra!", 2000);
bekleVeYazdir("Merhaba 1 saniye sonra!", 1000);

// Döngülerde closure kullanımı (dikkatli olunması gereken bir durum)
for (var i = 1; i <= 3; i++) {
    // var fonksiyon kapsamlı olduğu için, setTimeout çalıştığında i'nin son değeri (4) kullanılır.
    setTimeout(function() {
        console.log("Yanlış (var):", i); // Hep 4 yazar
    }, i * 500);
}

for (let j = 1; j <= 3; j++) {
    // let blok kapsamlı olduğu için, her döngü tekrarı için yeni bir j değişkeni oluşur.
    // setTimeout callback'i doğru j değerini closure ile yakalar.
    setTimeout(function() {
        console.log("Doğru (let):", j);
    }, j * 500 + 1500); // Diğer loglardan sonra çalışması için süre ekledik
}

// Çıktı (yaklaşık zamanlamayla):
// Merhaba 1 saniye sonra!
// Yanlış (var): 4
// Yanlış (var): 4
// Yanlış (var): 4
// Merhaba 2 saniye sonra!
// Doğru (let): 1
// Doğru (let): 2
// Doğru (let): 3

Uyarı: Döngüler içinde var ile closure kullanırken dikkatli olunmalıdır. var fonksiyon kapsamlı olduğu için, döngü bittikten sonra çalışan callback fonksiyonları genellikle döngü değişkeninin son değerini görür. Bu sorunu çözmek için let (blok kapsamlı) veya IIFE gibi teknikler kullanılabilir.

Alıştırmalar

  1. Global, fonksiyon ve blok kapsamlarını gösteren basit bir kod örneği yazın. Farklı kapsamlardaki değişkenlere erişmeye çalışın ve sonuçları yorumlayın.
  2. Bir "kullanıcı profili" oluşturan bir fonksiyon fabrikası yazın. Bu fabrika, kullanıcının adını ve yaşını parametre olarak almalı ve kullanıcının bilgilerini döndüren (getBilgi) ve yaşını artıran (yasArtir) metodlara sahip bir nesne döndürmelidir. Closure kullanarak ad ve yaş bilgilerini özel tutun.
  3. Bir dizi içindeki her eleman için 1 saniye arayla konsola mesaj yazdıran bir fonksiyon yazın. setTimeout ve closure kullanarak her elemanın doğru değerini yazdığından emin olun.
  4. Closure kullanarak sadece bir kez çalıştırılabilen bir fonksiyon oluşturan bir fonksiyon yazın. (İpucu: Fonksiyonun içinde bir bayrak değişkeni tutun).

Bu derste JavaScript'teki kapsam ve closure kavramlarını öğrendik:

  • Kapsam (Scope): Değişkenlerin ve fonksiyonların erişilebilirliğini belirler (Global, Fonksiyon, Blok).
  • Global Kapsam: Kodun her yerinden erişilebilir.
  • Fonksiyon Kapsamı: var ile tanımlanan değişkenler için geçerlidir, sadece fonksiyon içinden erişilebilir.
  • Blok Kapsamı: let ve const ile tanımlanan değişkenler için geçerlidir, sadece tanımlandıkları blok içinden erişilebilir.
  • Kapsam Zinciri: Değişken arama mekanizmasıdır, mevcut kapsamdan başlayarak global kapsama kadar devam eder.
  • Closure: Bir fonksiyonun, tanımlandığı kapsamdaki değişkenlere erişebilme ve onları "hatırlayabilme" yeteneğidir.
  • Closure Kullanım Alanları: Veri gizleme, fonksiyon fabrikaları, callback'ler ve asenkron işlemler gibi birçok desende kullanılır.

Kapsam ve closure, JavaScript'in temel ve güçlü kavramlarıdır. Bu kavramları iyi anlamak, daha modüler, güvenli ve etkili kod yazmanıza yardımcı olacaktır.

Bir sonraki bölümde, JavaScript'in önemli veri yapılarından olan diziler ve nesneleri daha detaylı inceleyeceğiz.