JavaScript: Bu ne perhiz, what is “this” Aman İsmail, can touch “this”
Merhaba bu yazımda; Javascript’te anlaşılması en karışık konulardan biri olan this
anahtar sözcüğünden bahsedeceğim. Konuya geçmeden başlığa adını veren Grup Vitamin’in İsmail şarkısını dinlemek isterseniz sizi şuraya alalım.
Yazıya direkt olarak bir örnekle başlamak istiyorum. Aşağıdaki örnekte kisi nesnesinin içerisinde bulunan yasHesapla
fonksiyonunu kullanarak, yaş bilgisini ekrana yazdırıyoruz. Çıktıya baktığımızda istediğimiz sonuç ekranda yazılmıyor. Garip değil mi? Yazının amacı işte bu garip(!) olan durumun altında yatan gizemi ortaya çıkarmak. Umarım başarabilirim. Hadi başlayalım.
Javascript, this
anahtar sözcüğünü C++ dan ödünç almıştır. Her iki dilde de this
anahtar sözcüğü; kendi özel sınıfından türetilmiş nesneye işaret eder. Buraya kadar her şey normal. JavaScript’te this
anahtar kelimesinin bir ikinci bir kullanım amacı/özelliği mevcuttur. this
anahtar kelimesi bir önceki görevinin yanı sıra Execution Context’i(genellikle bir fonksiyonun nerede çalıştırıldığına bağlıdır) izlemek için de kullanılır. Concext değiştikçe, this anahtar cümlesinin işaret ettiği nesne de değişir. Bu ikilik durumu işlerin karmaşıklaşmasına neden olmaktadır.
this anahtar sözcüğünün kullanımı
this
anahtar sözcüğü; Özel bir sınıfın yapıcı metodundan türetilmiş nesneye işaret eder.
Birinci örnekte Meyve fonksiyonu(Yapıcı Fonksiyon), Meyve tipindeki nesne için yapıcı metot olarak kullanıldı. Bu yüzden this anahtar kelimesi objenin kendisine referans etti. 18. satırdaki kullanım ile; Şu anda tanımladığımız nesne aslına bu(this)
nesne diyoruz. Yapıcı bir fonksiyon kullandığımızda, fonksiyonun tamamı yapıcı metot olur. Yukarıdaki bilgiler ışığında konsolda da görüldüğü gibi this
anahtar sözcüğü Meyve’nin kendisini işaret etmiştir.
elma
nesnesini Meyve nesnesinden türettikten sonra elma.renk özelliğine(property) ulaşabiliriz. Çünkü renk özelliği this
anahtar sözcüğüne ilerde yaratılacak her bir örneği için eklendi.
İkinci örnekte de benzer durum gözlenmiştir.
2. this
anahtar sözcüğü Execution Context( lexical scope veya lexical environment olarak ta adlandırılır) öğesini izler. Lexical Scope’ı , ilgili kapsamda tüm değişkenler için hafızada ayrılan konum olarak düşünebilirsiniz.
Birinci örneğimizde this
anahtar kelimesi farklı bir duruma büründü. Eğer bir fonksiyonu new
ile kullanmayıp sadece çalıştırırsa; this
anahtar kelimesi Global Scope’a referans eder. Çünkü varsayılan Execution Context global
’dir.(Browserlarda global scope [object Window]’dır). 2. örnekte olduğu gibi bir fonksiyon new anahtar sözcüğü ile kullanırsa, türetilen nesnenin kendisine referans eder.
Basit Fonksiyon çağrısı yapıldığında(new kullanılmadan),
this
anahtar kelimesi her zaman global nesnesine referans eder
- Execution Context bağlantısı, bir Callback fonksiyon tarafından belirtildiğinde,
this
anahtar kelimesi , bir nesnenin yapıcısının içinde tanımlanmış olsa bile kurulur.
Yukarıdaki örneğimizin 15. satırda çalıştırılan fonksiyonun çıktısındaki undefined
kelimesine dikkat etmişsinizdir. Tuhaf değil mi? Önce fonksiyonun içerisinde neler oluyor bunu anlayalım. 3. satırda calistir
özelliğini tanımlayıp araba
fonksiyonun içindeki 5. satırda bulunan calistir
fonksiyonunu kendisine atıyoruz. Bu satırda herhangi bir hata almıyoruz çünkü fonksiyonlar hoisted edildiği için tanımlamadan önce kendisini bir değişkene atayabiliriz. calistir
fonksiyonun içinde bulunan setTimeout
fonksiyonu ile arabayı parametre olarak verilen süre kadar çalıştırıyoruz.
15. satırdaki durumu açıklarsak. setTimeout
fonksiyonunu çağırdığımız zaman fonksiyon, araba nesnesi ile this anahtar kelimesi ile olan bağını koparır ve [object Window]’a referans eder. Bu sorunu çözmek için aşağıdaki gibi bir yöntem kullanabiliriz.
6. satırda this
değişkeni yeni bir değişkene atıyoruz. Çünkü calistir
fonksiyonu araba
fonksiyonuna referans ettiğinden, araba
fonksiyonun tüm özelliklerini yakalayabiliriz.
- Execution Context’te olan bu bağlantılı durumu, arrow function kullanarak ortadan kaldırabiliriz. Çünkü arrow fonksiyonların yapıcı metodu yoktur. Bu yüzden fonksiyonunda kendine ait bir context’i yoktur. arrow fonksiyonlarda
this
bir üst bloğa işaret eder.
6. satırda function() {}
ifadesini arrow syntax () => {}
ile değiştirdik. Bu sayede let that = this değişken atamasından kurtulmuş olduk.
Yazının başındaki örneğe dönüp orada tuhaf görünen durumu açıklayalım.
Örneğimizde 14. satırda kisi
nesnesinde bir özellik olarak tanımlanmış olan yasHesapla
fonksiyonu çağrılıyor. 7. satırda this
nesnesi çağrılıp içinde bulunan adi
bilgisi ekrana yazılıyor. Bu satırda this
nesnesi kisi nesnesine referans ettiğinden hata almadan adi
özelliğine ulaşılabilmiştir. yasHesapla
fonksiyonun içerisinde, 5. satırda yasiYaz
fonksiyonu tanımlanıyor ve 11. satırda bu fonksiyon çağrılıyor(basit fonksiyon çağrısı). Bu fonksiyonun içerisindeki this
, global nesnesine referans ettiğinden ve global nesnesi içerisinde yasi
özelliği bulunmadığından undefined
olarak ekrana yazılmıştır. Bu durum JavaScript’in bir bugı değil. Kural açık, basit fonksiyon çağrısı yapıldığında(new kullanılmadan) this nesnesi her zaman global nesnesine referans eder.
(İsteğe bağlı okuma bölümü)JavaScript’te her fonksiyonda varsayılan olarak bulunan call
, bind
ve apply
metotları ile this anahtar sözcüğünün nasıl davranış sergilediğine yakından bakalım.
call
, apply
, bind
metotları kendisine verilen parametreleri this
nesnesinin yeni değerlerini sunar. Bu sayede bir metodu, kendisine parametre olarak verilen nesneyi inherit
ederek, aynı metodu tekrar yazmadan kullanabilmemize olanak sağlar. Konuyu daha iyi anlamak için aşağıdaki örneğe bakalım:
Yukarıdaki örnekte Araba
nesnesinin hesapla
fonksiyonu bulunmamaktadır. hesapla
fonksiyonu call
ile inherit
olmuş ve this
nesnesi Araba
nesnesi için değerlerini sunmuştur.
KAPANIŞ
JavaScript’teki bu tarz konseptleri anlamak biraz zaman alır. O yüzden bol bol pratik yapmak gerekir.
Yazının içerisinde eksik, hatalı bir durumsa iletirseniz düzeltirim. Bir sonraki yazıda görüşmek üzere.