JavaScript’te “This” Kavramı
Herkese merhaba, bugün sizlere birçok kişi açısından belki de JavaScript’in en karmaşık konusu olan this keywordünden bahsetmeye çalışacağım. Önceki yazılarda biraz değinmiştik ama bugün biraz daha detaya inerek this’i anlamaya çalışalım.
This’e bağlı olarak teknik bir tanım yapacak olursak; this, bir fonksiyonun nasıl çağrıldığına bağlı olarak değişen söz konusu nesnedir. This’in JavaScript’te iki konsepti vardır.
1)obj.func() → this obj
Bir objeye bağlı olan fonksiyon çalıştırıldığı zaman (ki bunlara biz metot diyoruz) this, o an ki söz konusu olan objeye referans verir. (objeyi ne tanımlamışsak, örneğin person)
2)this → global object → window (global)
Diğer hemen hemen tüm durumlarda this, global object’e referans verir. (biz browser üzerinden çalıştığımız için window nesnesine referans verir)
Şimdi bir örnek vererek direkt console ekranı üzerinde this’i inceleyelim.
Yukarıdaki örnekte 1. satır ve çıktısına baktığımız zaman, biz browserda çalıştığımız için this global object’i yani window nesnesini gösterdiğini görüyoruz. 2. satıra baktığımızda ise window nesnesinin içerisindeki location özelliğini ekranda görüyoruz. Buna bağlı olarak 3. satırda this üzerinden 4. satırda ise window üzerinden location’ın href özelliğini ekrana yazdırdığımızda ikisinin de aynı çıktı verdiğini görüyoruz. Görmüş olduğumuz gibi genel kullanımda this, global object’e yani window object’e referans verir. Son bir ispat olarak 5. satırda direkt this ve window objesini karşılaştırdığımızda ise true sonucu alıyoruz.
Şimdi tanımımıza tekrar bakarsak bir fonksiyonun nasıl çağırıldığına bağlı olarak değişen söz konusu nesne demiştik. Biz fonksiyonları function expression ve function declaration olarak iki şekilde çağırıyorduk.
Şimdi yukarıdaki örneğe baktığımızda 2. ve 7. satırlarda iki farklı yöntemle fonksiyonlarımızı belirledik. Biz bu fonksiyonları çalıştırana kadar this’lik bir olay yok, bu fonksiyonlar çalıştıktan sonra this’in ne olduğuna karar veriyor JavaScript engine. Bu da aklımızda bulunsun şimdilik. 11. ve 12. satırda bu fonksiyonları çalıştırdığımızda yine window nesnesini gösteriyor. Yani function declaration veya function expression yöntemiyle bir fonksiyonu çalıştırdığımız zaman o fonksiyonun içerisindeki this hala window nesnesine referans veriyor. Yukarıdaki tanıma dönecek olursak eğer bir fonksiyon bir objenin metodu durumunda olduğu zaman this oradaki nesneye, diğer durumda global object’e yani window nesnesine referans veriyordu. Yukarıdaki örnekte gördüğümüz gibi her iki durumda da bir objenin metodu olmadığı için buradaki this’ler global nesneye referans veriyor. Şimdi işleri bir adım daha ileri götürelim o zaman.
Yukarıdaki örnekte 3. satırda func1'in içine this’e bağlı olarak name isminde bir özellik ekleyip değerini micheal olarak veriyoruz. İkinci olarak 9. satırda da age üzerinden aynı işlemleri yapıyoruz. Kaydedip console ekranında window object’i incelediğimizde age’in 40, name’in micheal olduğunu görüyoruz. Yani biz fonksiyonun içinde this’e bağlı olarak yeni bir property oluşturduğumuz zaman direkt gidip global nesneye bağlıyor. Çünkü buradaki this global nesneye yani window object’e referans veriyor.
Yukarıdaki örnekte 16. satıra bakacak olursak direkt console log ile age değişkenini ekrana yazdırdığımızda 40 sonucunu görüyoruz dikkat ederseniz. 17. satırda bu sefer başına this koyarak yazdırıyoruz ve ekranda tekrar 40 sonucunu alıyoruz. Neden? Çünkü buradaki this global nesneyi gösteriyor. 16. satırda biz this keywordünü kullanmasak bile name, globale yani window object’e bağlı olduğu için 40 sonucunu ekranda görüyoruz.
Yukarıdaki örneklerde fonksiyonun içinde bir özellik oluşturduğumuzda globale’e referans verdiğini gördük. Şimdi bununla ilgili bir örnek yaparak işi bir adım daha öteye taşıyalım.
Şimdi yukarıdaki örneğe baktığımızda bir undefined bir de michael sonucunu aldık. Adım adım analiz edecek olursak önce birinci fonksiyon (func1) çalıştı. Daha sonra name’e michael değerini atadı ancak 4. satıra geldiğimizde age’i ekrana yazdıramadı, undefined durumunda. Daha sonra ikinci fonksiyon çalıştı age’e 40'ı verdi. Daha sonra name’i ekranda gösterdiğimizde michael değerini yakaladı. Neden acaba? Bunun nedeni, function expression’da hoisting’in çalışmaması. Yukarıdaki örneğe bakarsak func1 fonksiyonu çalıştırıldığında micheal değerini name’e atıyor ancak age’i göstermeye çalıştığında undefined durumunda. Neden? Çünkü func2 bir function expression ve function expression’da hoisted yani değişkenlerin önce oluşturulması olmadığı için undefined durumunda kalıyor.
Hatta function expression’ı function declaration’ın üzerine alıp önce func2 fonksiyonunu çalıştıralım, bakalım nasıl bir sonuç alacağız.
Yukarıdaki örneğe baktığımızda bu sefer michael ve 40 değerlerini gördük, undefined’dan kurtulduk. İkinci fonksiyon artık this’i yakalayabiliyor. Çünkü function declaration hoisted yapabiliyor yani birinci fonksiyonu (func1) çalıştırmadan declare edebiliyoruz.
This keywordünün globaldeki yapısı için yeterince konuştuğumuzu düşünüyorum. Biraz da objeye bağlı fonksiyon yani metot çalıştığındaki this durumunu ele alalım. Bunun için işi en başından alarak constructor fonksiyon yardımıyla nesne oluşturma yöntemimizi inceleyelim.
Yukarıdaki örneğe baktığımızda 15. satırda michael isminde constructor fonksiyon yardımıyla bir nesne oluşturduk. Şimdi de person fonksiyonun detaylarına bakalım biraz. Biz öncelikle constructor fonksiyon oluşturduğumuzda obj isminde boş bir nesne oluşturuyoruz daha sonra parametreden gelen değerleri tek tek bu nesnenin bir özelliği olarak atıyoruz. Yani biz boş bir nesne oluşturuyoruz ve o an söz konusu olan nesne neyse özellikleri ona atamasını istiyoruz. Yukarıda biz michael nesnesi oluşturuyoruz o zaman obj nesnesi michael oluyor, eğer dwight isminde bir nesne oluştursaydık obj nesnesi dwight olacaktı. Bunu da bir örnekle görelim.
Yukarıdaki örnekte de gördüğümüz gibi 2. satırdaki obj nesnesi, o an ki söz konusu nesneyi yani current object’i ifade ediyor. İşte bunu daha kolay bir şekilde belirtmek için, söz konusu olanı yani bu olanı daha iyi belirtmek için this keywordünden yararlanıyoruz. Yukarıda yaptığımız örneği obj yerine this kullanarak yapalım.
Yukarıdaki örneğe bakacak olursak ilk olarak boş bir obj nesnesinden kurtulduk çünkü constructor fonksiyon kullanıyoruz burada aynı zamanda return obj’ye de gerek yok çünkü constructor fonksiyon kullanıyoruz nesne döneceğimiz belli ve son olarak obj yerine this keywordünü kullandığımızda aynı sonucu alıyoruz. Tabi bunu son versiyon olan class gösterimi ile ifade edecek olursak;
Yukarıdaki örneğe bakacak olursak artık function değil class keywordü kullanıyoruz. Class’ın içerisinde constructor metodu oluşturup parametreleri bu metoda alıyoruz. Daha sonra constructor metodu içinde gerekli this işlemlerini yapıyoruz. Neden? Çünkü constructor fonksiyon önce boş bir nesne oluşturur daha sonra o boş nesneye yani o an söz konusu nesneye this’i atar. 8. satıra bakacak olursak fullName’in önünde this olsaydı hata verirdi. Çünkü class’a ait olan metodu yazarken artık this kullanmıyoruz. Neden? Çünkü console ekranında gördüğümüz gibi bu fullName metodu zaten kendini prototype’ın içinde belirttiği için this’e gerek yok.
Şimdi biraz da object literal durumundan bahsedelim.
Yukarıdaki örnekte gördüğümüz gibi ilk 11. satırda zaten michael nesnesini yazdırdık. Ancak 12. satıra baktığımızda console log iki işlemi yapıyor. Birincisi this’i yazdırıyor ki burada this, michael nesnesine referans verir. Çünkü bir objenin içinde fonksiyon olarak yani metot olarak kullanılmıştır. İkinci olarak da name ve surname’i ekrana yazdırıyor. Şimdi bu örneğimizi bir adım daha ileri taşırsak her şeyin daha iyi anlaşılacağını düşünüyorum.
Yukarıda micheal nesnesine ek olarak friend isminde bir nesne daha ekledik ve orada da fullName fonksiyonu içinde bir this çalışıyor. 18. satırı çalıştırdığımızda zaten 6. satırdaki this’in michael nesnesine referans ettiğini görmüştük. Bu sefer 19. satırda friend’e ait fullName metodunu çalıştırdığımız zaman 13. satırdaki this, dikkat ettiyseniz jim nesnesine referans veriyor. Şimdi de farklı bir örnek yapalım.
Yukarıdaki örneğe baktığımızda 8. satırda true sonucunu görüyoruz ki bu zaten beklediğimiz bir sonuç. Çünkü bir nesnenin fonksiyonu şeklinde kullandığımız için doğal olarak this, michael nesnesine referans veriyor.
10. satırda fucny2 isminde bir değişkene funcy metodunu olduğu gibi atıyoruz. Daha sonra 11. satırda funcy2'yi çalıştırmaya çalıştığımız zaman bu sefer false alıyoruz. Yani this artık michael nesnesi değil window object’i referans veriyor. Biz en başta this tanımı için “eğer bir nesnenin fonksiyonu durumundaysa object, diğer durumda da globale’e referans verir” demiştik. Yani 10. satırdaki michael nesnesindeki funcy metodunu funcy2 değişkeni tanımlayıp daha sonra bu değişkenin içine atıyoruz ve bu değişkeni metot olarak çalıştırmaya çalıştığımız zaman this artık michael nesnesine değil window object’e referans veriyor. O zaman son bir örnek daha yapalım.
Yukarıdaki örneğe baktığımız zaman 12. satırda funcy metodunu çalıştırdığımız zaman this, michael nesnesine eşit dikkat ederseniz. Bu zaten beklediğimiz bir sonuç. Ancak 5. satırda setTimeout fonksiyonunun içinde ilginç şeyler oluyor. setTimeout’un içindeki this artık michael değil window object. Çünkü funcy metodu bir objenin içinde ancak setTimeout artık kendi başına çalışan bir anonymous fonksiyon. Kendi başına çalışan anonymous fonksiyonun this’i artık ilgili nesne değil global nesne oluyor.
Evet arkadaşlar bu yazıda sizlere JavaScript’in belki de en karmaşık konularından biri this kavramını elimden geldiği kadar sade bir şekilde açıklamaya çalıştım. Umarım faydalı olmuştur, iyi çalışmalar.
Referanslar;
1)Arin Yazılım
2)JavaScript MDN
3)JavaScript Info