Menu
Playground'da Dene

JavaScript this Kullanımı: Bağlama Kuralları ve Tuzaklar

JavaScript'te this aslında nasıl çalışır? Dört bağlama kuralı, arrow function'larda neden farklı davrandığı ve klasik 'this is undefined' tuzağından kurtulma yolları.

this Çağrı Anında Belirlenir

JavaScript'te this kullanımına dair kafa karışıklığının büyük çoğunluğu tek bir yanlış varsayımdan kaynaklanır: this'in fonksiyonun tanımlandığı yere bağlı olduğunu sanmak. Oysa durum böyle değil. this, fonksiyonun nasıl çağrıldığına göre belirlenir.

Aynı fonksiyonu iki farklı şekilde çağırdığımızda ne olduğuna bakalım:

index.js
Output
Click Run to see the output here.

Aynı fonksiyon, ama this farklı. İlk çağrıda noktadan önce user var, dolayısıyla this user oluyor. İkinci çağrıda noktadan önce hiçbir şey yok, bu yüzden this undefined. Fonksiyonun kendisi değişmedi — değişen tek şey çağrı biçimi.

Şunu aklınızın bir köşesinde tutun: this'in ne olduğunu anlamak için tanıma değil, çağrıya bakın.

Dört Bağlama (Binding) Kuralı

JavaScript'te this değerinin belirlenmesinin dört farklı yolu var. this ile ilgili aklınıza takılacak hemen hemen her sorunun cevabı, bu dört kuraldan hangisinin devreye girdiğini bulmaktan geçiyor.

1. Metot çağrısı: obj.fn()

Bir fonksiyon, bir nesnenin özelliği olarak çağrıldığında this o nesneye işaret eder:

index.js
Output
Click Run to see the output here.

Noktadan önceki şey this olur. Bu kadar basit.

2. Düz çağrı: fn()

Önünde herhangi bir nesne olmadan çağrıldığında, this strict mode'da (modüller ve sınıflar bunu otomatik olarak açar) undefined; sloppy mode'da ise global nesne olur:

index.js
Output
Click Run to see the output here.

İşte tam da bu noktada o meşhur "this is undefined" hatası karşımıza çıkıyor. Bir metodu ait olduğu nesneden söküp aldığınız anda, metot çağrısı sıradan bir fonksiyon çağrısına dönüşüyor:

index.js
Output
Click Run to see the output here.

counter. olmadan çağırdığımızda hiçbir bağlama kalmıyor. Fonksiyon, hangi nesneden geldiğini hatırlamıyor.

3. Açık binding: .call(), .apply(), .bind()

this değerini istediğin şeye zorla sabitleyebilirsin:

index.js
Output
Click Run to see the output here.

.call ve .apply fonksiyonu anında çalıştırır; aralarındaki tek fark, argümanları nasıl aldıklarıdır. .bind ise this değeri kalıcı olarak sabitlenmiş yeni bir fonksiyon döndürür — özellikle callback'lerde işe yarar.

4. new ile çağırma: new Fn()

Bir fonksiyonu new ile çağırdığınızda yepyeni bir nesne oluşturulur ve this bu nesneye bağlanır:

index.js
Output
Click Run to see the output here.

Sınıflar da arka planda bu mekanizmayı kullanır. İlerleyen bölümlerde sınıflara da değineceğiz.

Arrow Function'larda this Davranışı

Arrow function'lar yukarıdaki kuralları bilinçli olarak çiğner. this'i hiç bağlamazlar — onu tanımlandıkları andaki dış kapsamdan (scope) miras alırlar ve orada dondururlar:

index.js
Output
Click Run to see the output here.

Modül seviyesindeki ok fonksiyonu, modülün this değerini yakalar — ki bu da undefined'dır. user.arrow() şeklinde çağrılmış olsa bile, arrow function this'i yeniden bağlamayı reddeder.

Kulağa bug gibi geliyor ama aslında olayın bütün esprisi bu. Arrow function'lar asıl değerini metotların içinde gösterir; yani dıştaki this'i olduğu gibi korumak istediğinde:

index.js
Output
Click Run to see the output here.

setInterval içindeki ok fonksiyonu, this değerini start metodundan miras alır; start de timer.start() şeklinde çağrıldığı için this.seconds sorunsuz çalışır. Orada klasik bir function kullansaydık, o fonksiyonun kendi this'i olurdu (ki bu da setInterval'in ne verdiğine bağlıdır) ve kod çuvallardı.

Pratik kural: metotların içindeki callback'lerde ok fonksiyonu kullanın. Metotların kendisi için normal fonksiyonlarda kalın.

Klasik Callback Tuzağı

this'in en sık tökezlediği yer burasıdır. Bir metodu callback olarak geçirdiğiniz anda bağlamını kaybeder:

index.js
Output
Click Run to see the output here.

setTimeout, fonksiyonu c.increment() şeklinde değil, düz bir çağrıyla tetikliyor. Bunu çözmenin üç yolu var:

index.js
Output
Click Run to see the output here.

Üçü de işini görür. Genelde en anlaşılır olanı arrow function ile sarmalamaktır.

Üst seviyede this

Üst seviyedeki this'in değeri, kodun nerede çalıştığına göre değişir:

  • Tarayıcıda script (module değil): this, window'u gösterir.
  • ES module (yani modern bundle'lanmış kodların büyük kısmı): this, undefined'dır.
  • Node.js CommonJS modülü: this, module.exports'a karşılık gelir.

Ortamdan bağımsız şekilde global nesneye güvenilir biçimde erişmek istiyorsan globalThis kullan:

index.js
Output
Click Run to see the output here.

Pratikte üst seviyedeki this'e bel bağlamamak en sağlıklısı. Gerçekten global nesneye erişmen gerekiyorsa globalThis kullan; onun dışında değerleri açıkça parametre olarak dolaştırmak çok daha temiz olur.

Hızlı Karar Ağacı

Bir yerde this gördüğünde ne olacağından emin değilsen, şu listeyi sırasıyla uygula:

  1. Fonksiyon bir arrow function mı? O zaman this, dış kapsamdaki this ne ise odur. Çağrının nereden yapıldığı hiç önemli değil.
  2. new ile mi çağrıldı? O zaman this, yeni oluşturulan nesnedir.
  3. .call, .apply ile ya da bind edilmiş bir fonksiyon olarak mı çağrıldı? O zaman this, oraya verilen değerdir.
  4. obj.method() şeklinde mi çağrıldı? O zaman this, obj'dir.
  5. Düpedüz fn() olarak mı çağrıldı? Strict mode'da this, undefined olur.

Bu sıralama, tam da bu sırayla, her durumu çözer.

Sırada: Higher-Order Functions

Artık this senin için bir muamma olmaktan çıktığına göre, JavaScript'i bu kadar etkili yapan kalıba geçebiliriz: fonksiyonları değer gibi bir yerden bir yere taşımak. Bir sonraki konumuz higher-order functions — yani başka fonksiyonları parametre olarak alan ya da döndüren fonksiyonlar — ve bunların array metotlarına, event handler'lara ve gerçek dünyadaki JavaScript kodunun büyük kısmına nasıl güç verdiği.

Sıkça Sorulan Sorular

JavaScript'te this neyi ifade eder?

this, fonksiyonun tanımlandığı yeri değil, çağrıldığı nesneyi işaret eder. Örneğin user.greet() çağrısında this değeri user olur. Ama düz bir greet() çağrısında strict mode'da this undefined, sloppy mode'da ise global nesnedir. Yani kural basit: tanım yeri değil, çağrı yeri belirleyici.

Fonksiyonumun içinde this neden undefined oluyor?

Büyük ihtimalle bir metodu nesnesinden kopardınız ve tek başına çağırdınız ya da callback olarak ilettiniz. const fn = user.greet; fn(); yazdığınız anda bağlama kaybolur — çünkü çağrı sırasında noktanın solunda bir nesne yok. Çözüm için .bind(user) kullanabilir, arrow function ile sarmalayabilir veya doğrudan user.greet() şeklinde çağırabilirsiniz.

Arrow function'larda this nasıl farklı davranır?

Arrow function'ların kendi this değeri yoktur. Tanımlandıkları andaki dış kapsamın this değerini miras alırlar. Bu yüzden bir metodun içindeki callback'lerde dıştaki this'i korumak istediğinizde biçilmiş kaftandır. Bunun bir sonucu da şu: .call(), .apply() ve .bind() bir arrow function'ın this değerini değiştiremez.

Script'in en üst seviyesinde this nedir?

Tarayıcıda normal bir script'te en üst seviyedeki this, window nesnesidir. ES modüllerinde undefined döner. Node.js CommonJS modüllerinde ise module.exports'u işaret eder. Ortamdan bağımsız, her zaman global nesneyi veren referans ise globalThis'tir.

Coddy ile kodlamayı öğren

BAŞLA