Yazılım.
CevapSitesi.com Beta!
Çözüm Noktası
Facebook, Twitter, Google+ veya e-posta ile paylaşın.
| Sorular | Makaleler | Üyeler | Etiketler  | İletişim
Soru sormak ya da cevap vermek için;
giriş yapın veya üye olun.

Sosyal medya hesaplarınızla da giriş yapabilirsiniz.
0

JavaScript Fonksiyonları: Kapsanımlar - Closures ve Geri Çağırma Fonksiyonları - Callback


JavaScript fonksiyonları içinde tanımlanan bir fonksiyon veya bir fonksiyon içinde başka bir fonksiyona parametre olarak gönderilen fonksiyonlar içinde tanımlandıkları fonksiyonların kapsamına dahildirler. Bu yüzden içinde tanımlandıkları fonksiyonun kapsamındaki değişkenleri (değişken kapsamını) ve değerleri kullanabilirler.

Bu da JavaScript programlamada sık kullanılan çok yararlı bir özelliktir. Eğer JQuery, Node.js vb. bir kütüphane veya teknoloji kullanıyorsanız bunu çok iyi kavramanız gerekir.

Örnek:

function mesaj() {
	var metin = "Merhaba";
	function mesajGoster() {
		console.log(metin);
	}
	return mesajGoster; // Fonksiyonu döndürüyor. () kullanılmadığına dikkat edin. Kullansaydık, fonksiyonu çalıştırıp sonucunu döndürmüş olurduk.
}


var fonksiyon = mesaj();
fonksiyon();

Çıktısı:

Merhaba

Örnekte mesaj isimli fonksiyonun döndürdüğü mesajGoster fonksiyonu, mesaj fonksiyonunun içinde tanımlanmış bir fonksiyon. Bu fonksiyonu fonksiyon isimli bir değişkene atıyoruz ve daha sonra çalıştırıyoruz. fonksiyon() şeklindeki çağrımız, fonksiyon değerini aldığı (daha önce de belirtiğimiz gibi, fonksiyonlar birer değerdir) asıl fonksiyon olan mesajGoster fonksiyonunu çalıştırır.

Dikkat edeceğiniz nokta şu. Normalde fonksiyon()  ifadesinin bulunduğu yerde metin değişkeni tanımlanmamaktadır. Ancak metin değişkeni ile asıl fonksiyon aynı yerde tanımlandıkları için fonksiyon dışarıdan çalıştırıldığında metin değişkeninin değerini tanıyabilmektedir.

Aynı örneği şu şekilde de yazabiliriz:

function mesaj() {
	var metin = "Merhaba";
	return function mesajGoster() {
		console.log(metin);
	}; // Tanımlama değil bir ifade olduğundan ; kullanıldı.
}

mesaj()();

Burada mesaj()  ifadesi fonksiyon döndürdüğünden bulunduğu yerde bir fonksiyon olduğunu düşünün. İkinci () ise bu fonksiyonu çalıştırır.


Şimdi bir de bir fonksiyon içinde başka bir fonksiyona parametre olarak gönderilen fonksiyonlarda kapsanıma örnek verelim.

Yukarıda da belirttiğimiz gibi JQery kütüphanesinde kapsanımlar çok sık kullanılır. Daha önceki makalede verdiğimiz JQery AJAX örneğini biraz geliştirelim ve sonra açıklayalım.

function istekYap() {
	
	var istekZamani = new Date();
	
	$.get("ajaxSayfam.html",
          function( gelenVeri ) {
              console.log("İstek zamanı:" + istekZamani + " Gelen veri:" + gelenVeri);
          });
	  
	console.log("İstek yapıldı.");
	
}

istekYap();

ajaxSayfam.html ile Merhaba metninin döndüğünü varsaydığımızda örnek çıktı:

İstek yapıldı.
İstek zamanı:Wed Jun 15 2016 02:05:52 GMT+0300 Gelen veri:Merhaba.

Burada görüldüğü gibi $.get fonksiyonu istekYap fonksiyonu içinde kullanılmış ve isteğe cevap geldiğinde çalıştırılacak fonksiyon olarak (ikinci parametresi) bir fonksiyon bildirilmiştir. Bu fonksiyon istek tamamlandığında çalıştırılacaktır, en alttaki istekYap() çağrısında çalıştırılmaz. Bu çağrıda sadece $.get'e daha sonra kullanılması için gönderilmiş olur. Ama ne zaman çalıştırılırsa çalıştırılsın istekYap fonksiyonu içinde tanımlanmış olduğundan bu fonksiyon içindeki istekZamani isimli değişkeni de kullanabilmektedir.

Örneğin çıktısında "İstek yapıldı." metninin istek tamamlandığında oluşturulan çıktıdan önce olduğuna dikkat edin. Bu, parametre olarak gönderilen fonksiyonun sonra (veri geldiğinde) çalıştığını göstermektedir. Çıktıyı sağlayan console.log$.get çağrısının hemen arkasından çalıştırılır. Fonksiyon ise veri geldiğinde çalıştırılacaktır.

Parametre olarak gönderilen bu fonksiyon, bir callback'dir. callback'in açıklaması için Javascript Foksiyonları: Fonksiyon İfadeler - Function Expressions makalemize bakabilirsiniz.

Kapsanımlar, eşzamanlı olmayan (asynchronous) çağrılarda çok işe yararlar. Bir tane de basit bir eşzamanlı olmayan örnek verelim.

function azSonraKareHesapla(sayi, fonksiyon) {
    var kare = sayi * sayi;
    setTimeout(function () { fonksiyon(kare); }, 2000);

}

function hesapla(sayi) {
    azSonraKareHesapla(sayi, function (karesi) {
                                console.log(sayi + " sayısının karesi = " + karesi);
                             });
}

hesapla(3);

İki saniye sonra göreceğiniz çıktı:

3 sayısının karesi = 9

hesapla  fonksiyonuna parametre olarak gelen değer (3) bu fonksiyon içinden azSonraKareHesapla fonksiyonununa bir fonksiyon ile beraber gönderiliyor. azSonraKareHesapla fonksiyonu, sayının karesini hesapladıktan sonra zamanlayıcı ile iki saniye sonra kendisine parametre olarak gelen fonksiyonu çağırıyor. Dolayısıyla iki saniye sonra kare hesaplanıp sonuç gösteriliyor.

Bu örneği üstteki JQuery örneği ile karşılaştıracak olursak, azSonraHesapla fonksiyonunu $.get fonksiyonunun yerine koyabilirsiniz. azSonraKareHesapla fonksiyonunun işini bitirdiğinde çağıracağı, bizim parametre olarak gönderdiğimiz fonksiyonu da $.get ile kullandığımız bitiş fonksiyonu olarak kabul edebiliriz.

azSonraKareHesapla fonksiyonunun içinde setTimeout  için tanımlanan fonksiyonda kare değişkeninin ve hesapla fonksiyonunun parametresi olarak gönderilen sayı değişkeninin azSonraHesapla  fonksiyonuna gönderilen parametre fonksiyon içinden tanındığına dikkat edin.

Closure ve Callback Örneği

        
/**
 * Uzak bir resmin var olup olmadığını tespit eden, eşzamanlı çalışmayan bir fonksiyon.
 * @yol {String} Kontrol edilecek uzak resmin adresi. 
 * @func {Function} Kontrol sonucunda resmin mevcut olup olmadığını döndürmek için kullanılan geri çağırma (callback) fonksiyonu.
 * @return {undefined} Bir dönüş değeri yok. Fonksiyon gönderilen geri çağırma fonksiyonunu çalıştırarak değer aktarır.
 */        


function uzakResimVarMi(yol, func) {
    var image = new Image; // Bir resim nesnesi tanımla.
    // Resim nesnesinin onload (yüklendiğinde) olayına bir ifade (expression) fonksiyon ata.
    // (Bu fonksiyon şimdi çalışmaz. Resim yüklendiği zaman tarayıcı tarafından çağırılır.)
    image.onload = function (e) { // e parametresi var ama örnekte kullanılmıyor. Yazmak şart değil.
    
        // Buraya geldiysek resim yüklenmiştir. Kontrol ediyoruz.
    
        // Genişlik ve yükseklik değerleri yoksa...
        if (this.width + this.height == 0) {
            this.onerror(); // Image nesnesinin onerror olayını elle / manüel çağır...
            return; // onload olay fonksiyonundan çık.
        }
        // Üstteki if'e takılmadıysak resim bir genişlik ve yüksekliğe sahiptir,
        // parametre olarak gönderilen func isimli geri çağırma fonksiyonunu (callback)
        // true parametresi ile çağır / çalıştır.
        // (Bu bir closure kullanımıdır. func parametre değişkeni bu fonksiyon içinde
        // tanımlanmadığı halde buradan kullanılabiliyor.)
        func(true);

    }; // Resim nesnesinin onload olayına atanan fonksiyonun sonu.
    
    // Resim nesnesine onerror (hata olduğunda) olayına bir ifade fonksiyon ata.
    // onerror olayı, üstteki onload fonksiyonu içinden çağırılıyor. Ancak
    // bir hata olduğunda tarayıcı kendi de bunu çalıştırabilir. Sonuç olarak
    // her iki durumda da parametre olarak gönderilen func isimli fonksiyonu
    // false değeri ile çağırır.
    // (Bu fonksiyon da şimdi çalışmaz. Resim yüklemesinde hata ortaya çıktığında veya
    // onload içinden yaptığımız çağrı ile çalışır.)
    image.onerror = function (e) { // e parametresi var ama örnekte kullanılmıyor. Yazmak şart değil.
        // Parametre olarak gönderilen func isimli geri çağırma fonksiyonunu (callback) false parametresi ile çağır / çalıştır.
        // (Bu bir closure kullanımıdır. func parametre değişkeni bu fonksiyon içinde
        // tanımlanmadığı halde buradan kullanılabiliyor.)
        func(false); 
    }; // Hata fonksiyonunun sonu.
    
    // Resmin src özelliğine yol parametresi ile gönderilen değeri ata.
    // Üstteki onload ve gerekirse onerror olay fonksiyonları bu atamadan sonra
    // ve resmin yüklenmesi / yükleme denemesi tamamlandıktan sonra çalışacaklar.
    image.src = yol;
    
}

// Kullanımı

// uzakResimVarMi fonksiyonuna 2. parametre olarak gönderilen fonksiyon ifadesi bir geri çağırma
// (callback) fonksiyonudur.
uzakResimVarMi("http://cevapsitesi.com/resimler/CS_Logo_48x48_BeyazYuvarlakZemin.png", function(sonuc) {
    if (sonuc)
        console.log("Resim var.")
    else
        console.log("Resim yok.")
});

// Veya şu şekilde de kullanılabilir.

/*

function uzakResimVarMiFonksiyonu(sonuc) {
    if (sonuc)
        console.log("Resim var.")
    else
        console.log("Resim yok.")
}
uzakResimVarMi("http://cevapsitesi.com/resimler/CS_Logo_48x48_BeyazYuvarlakZemin.png", uzakResimVarMiFonksiyonu);

*/


JavaScript Fonksiyonları ile İlgili Makaleler: