Hatalar, Bir Türü Olan Nesnelerdir
JavaScript bir hata fırlattığında size sadece düz bir metin vermez; aslında bir nesne verir. Bu nesnenin bir türü (yani constructor'ı) ve birkaç standart özelliği vardır: name, message ve stack.
err.name, "TypeError" gibi kısa bir etikettir. err.message ise insanın okuyabileceği açıklamadır. err instanceof TypeError ile de hatanın hangi sınıfa ait olduğunu öğrenirsin. Hatanın türünü bilmek önemli: sorunun bir yazım hatası mı, geçersiz bir değer mi, yoksa hiç parse bile edilememiş bir kod mu olduğunu sana söyler.
JavaScript'te yedi adet yerleşik hata türü vardır. Üçüyle sürekli karşılaşırsın, dördüyle ise arada sırada.
SyntaxError: Kod Parse Edilemedi
SyntaxError, JavaScript'in kodunu okuyamadığı anlamına gelir. Aslında bu tam olarak bir çalışma zamanı hatası değildir — motor, tek bir satır bile çalışmadan, parse aşamasında patlar. Bu yüzden aynı dosya içinde SyntaxError'ı try/catch ile yakalayamazsın; çünkü dosyanın tamamı baştan reddedilir.
function greet(name {
return "hi, " + name;
}
// SyntaxError: Unexpected token '{'
Eksik parantez, kaçak bir virgül ya da fonksiyon dışında kalmış bir return — dilin gramerini bozan her şey bu hatayı fırlatır. Çözüm her zaman aynı: kaynak kodu düzeltmek. SyntaxError'ı gerçekten yakalayabileceğin tek yer ise çalışma zamanında ayrıştırma yapılan durumlar, örneğin JSON.parse:
JSON.parse çalışma zamanında bir string aldığı için ondan gelen sözdizimi hatalarını try/catch ile yakalayabilirsin. Ama kendi kaynak dosyalarındaki SyntaxError'ları yakalayamazsın.
ReferenceError: Böyle Bir Değişken Yok
ReferenceError, mevcut kodun görebildiği hiçbir kapsamda (scope) tanımlanmamış bir değişkene erişmeye çalıştığında fırlatılır.
Onda dokuz, bunun sebebi basit bir yazım hatasıdır (total yerine totl yazmak gibi). Kalan yüzde onluk kısımda ise olay kapsamla (scope) ilgilidir — farklı bir fonksiyonda ya da modülde tanımladığın bir şeyi kullanmaya çalışıyorsundur.
Bir de daha sinsi bir sebep var: temporal dead zone (geçici ölü bölge). let ve const ile yapılan tanımlamalar, bulundukları bloğun en tepesinden itibaren var olur; ama tanımlandıkları satıra gelinmeden onlara erişemezsin:
console.log(x) çalıştığı anda x aslında gerçek bir binding olmuş durumda; ama henüz initialize edilmemiş. İşte reference error'un sebebi bu. Çözüm de basit: erişimi declaration'ın altına taşımak.
TypeError: Değerin Tipi Beklenenle Uyuşmuyor
JavaScript'te TypeError, değerin var olduğunu ama yapılmak istenen işleme uygun türde olmadığını gösterir. Function olmayan bir şeyi çağırmak, null ya da undefined üzerinden property okumak, bir const'a yeniden atama yapmak — hepsi birer TypeError.
Cannot read properties of null (reading 'name') ifadesindeki "Cannot read properties of null" hatası, JavaScript'te karşılaşacağınız muhtemelen en yaygın hata mesajıdır. Çözüm ya değerin mevcut olduğundan emin olmak ya da erişimi optional chaining ile korumaktır: user?.name.
TypeError'ın diğer çeşitleri:
Bir sayıyı fonksiyon gibi çağırmak, const bir değişkene yeniden atama yapmak ya da var olmayan bir metodu çağırmak — kısacası değerin türü, ondan yapmasını istediğin şeye uygun değil.
RangeError: Sayı İzin Verilen Aralığın Dışında
RangeError, bir sayı teknik olarak geçerli olsa da belirli bir işlem için izin verilen aralığın dışına çıktığında fırlatılır.
Klasik örneği sonsuz rekürsiyondur; çağrı yığınını (call stack) taşırır:
"Maximum call stack size exceeded" hatası neredeyse her zaman ya bir fonksiyonun kendisini base case olmadan çağırmasından ya da iki fonksiyonun birbirini sonsuz döngüde çağırmasından kaynaklanır.
URIError ve EvalError: Nadir Görülenler
URIError, URI ile ilgili fonksiyonlara (encodeURI, decodeURIComponent ve benzerleri) bozuk bir girdi verdiğinizde karşınıza çıkar:
EvalError artık tarihe karışmış bir hata türü. Modern JavaScript motorları bu hatayı aslında hiçbir durumda fırlatmıyor; geriye dönük uyumluluk adına sadece bir constructor olarak duruyor. Elle oluşturabilirsiniz, ama gerçek hayatta karşınıza çıkmaz.
Hata sınıflarının kalıtım zinciri
Saydığımız tüm hata türleri temel Error sınıfından türüyor. Yani hangisi olursa olsun err instanceof Error ifadesi true döner. Bu da genel bir catch bloğunda işinizi bir hayli kolaylaştırır:
catch bloğu her şeyi yakalar — biri throw "oops" yazdıysa onu bile. İşte bu yüzden son daldaki kontrol önemli. Yakaladığınız değeri error nesnesi gibi kullanmadan önce mutlaka instanceof ile türünü daraltın.
Bilinçli olarak hata fırlatmak
Kodunuz bir sorun tespit ettiğinde, yerleşik hata türlerinden herhangi birini kendiniz de fırlatabilirsiniz. Sadece yaşanan soruna en uygun türü seçin:
Hata tipini başarısızlığa göre doğru seçmek sadece kozmetik bir detay değil; çağıran tarafın hata mesajlarını string olarak parse etmek zorunda kalmadan, hedefe yönelik catch blokları yazabilmesini sağlıyor.
Özel Error Sınıfları (Custom Error Class)
Yerleşik hata tipleri ihtiyacınızı karşılamıyorsa Error sınıfını extend edin:
İki nokta aklınızda olsun: super(message) çağrısını yapın ki temel Error sınıfı düzgün kurulabilsin ve this.name değerini atayın ki loglarda doğru etiket görünsün. field gibi özel alanlar da çağıran tarafın, string içinde arama yapmak zorunda kalmadan belirli hata durumlarına tepki vermesini sağlar.
Sırada: Console ve DevTools
Hata türlerini bilmek işin yarısı — diğer yarısı ise stack trace okumak ve program çalışırken state'e bakabilmek. Tarayıcının devtools'u (ve Node'un debugger'ı), "bir hata fırlattı ama nedenini bilmiyorum" durumunu birkaç saniyelik bir incelemeye çeviriyor. Sıradaki konumuz bu.
Sıkça Sorulan Sorular
JavaScript'te yerleşik hata türleri hangileri?
JavaScript'te toplam yedi tane var: temel sınıf olan Error, ardından SyntaxError, ReferenceError, TypeError, RangeError, URIError ve EvalError. Her biri; name, message ve stack özelliklerine sahip bir hata nesnesi üreten bir constructor. Uygulamada en sık karşılaşacakların SyntaxError, ReferenceError ve TypeError olacak.
SyntaxError ile TypeError arasındaki fark ne?
SyntaxError, kodun geçerli JavaScript olmadığı anlamına gelir — motor kodu ayrıştıramıyor (parse edemiyor) bile. TypeError ise kod sorunsuz parse edilmiş ama çalışma zamanında geçersiz bir iş yapılmış demektir: fonksiyon olmayan bir şeyi çağırmak ya da null üzerinden bir property okumak gibi. Syntax hataları tüm script'i baştan durdurur; type hataları ise yalnızca sorunlu satır çalıştığında fırlatılır.
JavaScript'te ReferenceError ne zaman alırım?
Tanımlanmamış bir isim kullandığında veya bir let/const değişkenine temporal dead zone içinde (tanımdan önce) eriştiğinde. En yaygın sebebi yazım hataları: consoel.log(x) yazdığında ReferenceError: consoel is not defined fırlar. Önce yazımı ve scope'u kontrol et.
Kendi hata türlerimi oluşturabilir miyim?
Tabii ki. Yerleşik Error sınıfını extend etmen yeterli: class ValidationError extends Error { }. Constructor içinde this.name değerini ayarlamayı unutma; böylece loglarda ve catch bloklarında hatayı ayırt edebilirsin. Farklı hata durumları farklı şekilde ele alınacaksa custom error sınıfları kendini fazlasıyla ödetir.