Sorun: İç İçe Nesnelere Erişim Kırılgandır
İç içe geçmiş bir nesnenin derinlerine inmek güzeldir — ta ki aradaki bir katman ortada olmayana kadar:
user.address değeri undefined olduğu için undefined üzerinden .city okumaya çalıştığınızda TypeError: Cannot read properties of undefined hatası fırlatılır. Gerçek hayattaki veriler — API yanıtları, parse edilen JSON, DOM sorguları — olabilecek ya da olmayabilecek alanlarla dolu ve her seviye için tek tek savunmacı kontroller yazmak kısa sürede göz yorucu bir hal alıyor:
const city = user && user.address && user.address.city;
İki seviyeye kadar idare eder. Ama dört seviyeye inince işkenceye döner. ?. operatörü bunun en temiz çözümü.
?. Operatörü null ve undefined Durumunda Kısa Devre Yapar
Optional chaining, bir özelliği yalnızca kendisinden önceki değer null veya undefined değilse okur. Eğer öyleyse ifadenin tamamı undefined olur ve orada durur.
Birinci satır name değerini normal şekilde okur. İkinci satırda user.address undefined olduğu için işlem orada kesilir — zincirin geri kalanı hiç çalışmaz. Üçüncü satır ?. olmadan hata fırlatırdı çünkü undefined üzerinde .toUpperCase() çağrılmış olurdu; ?. sayesinde tüm ifade sessizce undefined değerini döndürür.
Akılda kalıcı bir özet: ?. operatörü "solumdaki değer null veya undefined ise pes et ve undefined döndür, değilse devam et" demektir.
Dizilerde ve Fonksiyon Çağrılarında da Çalışır
Üç farklı kullanım, aynı mantık:
?.[...]dizilere veya dinamik property erişimi için kullanılır.?.()fonksiyon olup olmadığı belli olmayan bir şeyi çağırmak için.?.nameise klasik property erişimi için.
Üçü de aynı mantıkla kısa devre yapar. Mesela user?.notAMethod?.() — notAMethod diye bir şey olmamasına rağmen hata fırlatmaz; ikinci ?. undefined değerini görüp orada durur.
Bu yaklaşım özellikle opsiyonel callback'lerde işe yarıyor:
Artık her çağrı öncesinde if (typeof onDone === "function") kontrolü yazmaya gerek yok.
Sadece null ve undefined Kısa Devreyi Tetikler
Çoğu kişinin kafasını karıştıran nokta tam da burası. ?. operatörü yalnızca nullish değerlere takılır. Diğer falsy değerler — 0, "", false, NaN — zincirlemeye devam etmek için gayet geçerli sayılır (tabii autoboxing'in izin verdiği ölçüde):
data.count değeri 0. Bu değer falsy olsa da nullish değil; dolayısıyla ?.toFixed(2) çağrısı çalışır ve sonuç "0.00" olur. Şimdi bunu && ile karşılaştıralım:
&& versiyonu 0 döndürür çünkü data.count falsy kabul edilir ve kısa devre yapar. ?. versiyonu ise "0.00" döndürür, çünkü 0 nullish değildir. Gerçekten 0'da durmak istiyorsan && doğru tercih. Ama sadece "değer yok" durumunda durmak istiyorsan aradığın şey ?..
? Nereye Konursa Fark Eder
?. kendisinden önceki değeri korur — sonrakini değil. Bu yüzden ?. işaretini eksik olabilecek seviyeye koymalısın:
Burada üç kullanım da çalışıyor çünkü aslında eksik bir şey yok. Ama config.server değeri undefined olabiliyorsa config.server?.host yazman gerekir — server'dan önce ?. koymak işe yaramaz, çünkü asıl sorun var olmayan bir server'ın .host özelliğini okumaya çalışmak.
İyi bir kural şu: ?. operatörünü yalnızca solundaki değerin gerçekten null ya da undefined olabileceği her seviyeye koy. Her noktaya "ne olur ne olmaz" diye ?. serpiştirmek hataları gizler ve kodu gereksiz yere kalabalıklaştırır.
?. ile Atama Yapılamaz
Optional chaining sadece okuma amaçlıdır. Bir atamanın sol tarafında kullanamazsın:
user?.address?.city = "Paris"; // SyntaxError
Aslında mantıklı: undefined olan bir şeyin property'sine değer atamak ne anlama gelirdi ki? Bir değeri yalnızca üst nesne varsa atamak istiyorsan, bunu açıkça yazman gerekir:
Ne Zaman Kullanmalı, Ne Zaman Uzak Durmalı
?. gerçekten opsiyonel olan değerlerde parlar:
- Alanların bazen gelip bazen gelmediği API yanıtlarında.
- DOM sorgularında:
document.querySelector(".banner")?.remove(). - Her zaman verilmeyebilecek callback'lerde:
options.onError?.(err). - Ara sonuçların
nullolabildiği zincirleme kütüphane çağrılarında.
Ama değerin her zaman var olması gereken yerlerde yanlış araçtır. Hataları susturmak için sağa sola ?. serpiştirmek, gürültülü ve kolay bulunabilir bir hatayı ("42. satırda TypeError") sessiz bir hataya dönüştürür (üç fonksiyon ötede gizemli bir şekilde undefined olan bir değişken). Bir şey orada olmak zorundaysa, bırak hata fırlatsın — o stack trace aslında sana iyilik yapıyor.
Gerçekçi Bir Örnek
Eksik gelebilecek bir API yanıtından bir değer çekelim:
Her ?. belirsizliğin bir seviyesini halleder. avatarUrl temiz bir şekilde undefined olur. onClick?.() ise handler varsa onu çağırır.
displayName satırındaki ?? operatörünü fark etmişsindir — bu da aynı kalıbın diğer yarısı. ?. bir şey eksikse sana undefined döndürür; ?? ise 0 veya "" gibi meşru falsy değerlere takılmadan varsayılan bir değer atamanı sağlar.
Sırada: Nullish Coalescing
?. ve ?? aslında aynı fikrin farklı problemlere uygulanmış hâli: ikisi de null ve undefined değerlerini "eksik" kabul eder, diğer falsy değerlere dokunmaz. Sonraki bölümde ?? ile nasıl mantıklı varsayılan değerler belirleyebileceğini — ve || operatörünün yıllardır bu işi neden sessiz sedasız yanlış yaptığını göreceğiz.
Sıkça Sorulan Sorular
JavaScript'te ?. ne işe yarar?
?. operatörü; kendisinden önceki değer null veya undefined değilse bir özelliğe, dizi indeksine ya da metoda erişir. Eğer değer bu ikisinden biriyse ifade hemen kısa devre yapar ve hata fırlatmak yerine undefined döner. Yani user?.address?.city yazdığında, user ya da address yoksa kod patlamaz.
Optional chaining'i ne zaman kullanmalıyım?
?. operatörünü, bir değerin olmamasının gerçekten normal olduğu durumlarda kullan: opsiyonel alanlar dönen API cevapları, her zaman bulamayabileceğin DOM sorguları, zorunlu olmayan callback'ler gibi. Ama her zaman var olması gereken bir değerdeki eksikliği gizlemek için kullanma — o tür durumlarda eksik değer aslında görmek istediğin bir bug sinyalidir.
?. ile && arasındaki fark nedir?
Çoğu durumda aynı sonucu verirler ama önemli bir fark var: ?. sadece null ve undefined için kısa devre yapar, && ise tüm falsy değerlerde — 0, '', false dahil — kısa devre yapar. Örneğin count değeri 0 iken obj && obj.count sana 0 döner; obj?.count da 0 döner ama zincir sadece nullish değerlerde temiz bir şekilde durur.
Optional chaining'i dizilerde ve fonksiyon çağrılarında kullanabilir miyim?
Evet. arr?.[0] ile bir dizi indeksine güvenli şekilde erişebilir, fn?.() ile de sadece tanımlıysa bir fonksiyonu çağırabilirsin. İkisi de aynı kuralı takip eder: ?. öncesindeki değer null ya da undefined ise ifade undefined olur ve sonrasındaki hiçbir şey çalışmaz.