Blok 2 · Ders 13
Hatalarla barışmak: try, catch, throw
Programın çökmeden hatayı yakalamasını, kendi hata mesajlarını üretmeyi ve yığın izini (stack trace) okumayı öğreniyorsun.
Bu dersin sonunda
- Çalışma zamanı hatasının programı neden durdurduğunu açıklayabilir,
try/catchile yakalayıp programı sürdürebilirsin - Hata objesinin
name,messagevestackalanlarını okuyup doğru yerde kullanabilirsin throw new Error(...)ile kendi hatanı fırlatabilir,finallyile her durumda çalışan temizliği yazabilirsin- Birkaç fonksiyonun birbirini çağırdığı bir programın yığın izini satır satır okuyup hatanın kaynağını bulabilirsin
- Fonksiyonlarına gelen değerleri erken doğrulayıp açık hata mesajları üretebilirsin
- Hangi hatayı yakalayacağına, hangisini yeniden fırlatacağına karar verebilirsin
Bu sayfada
- Çalışma zamanı hatası: program yolda düşer
- try / catch: dene, olmazsa yakala
- Hata objesi: name, message, stack
- throw: kendi hatanı fırlat
- finally: her durumda çalışan temizlik
- Yığın izini okumak
- Girdi doğrulama: erken ve açık hata
- Ne zaman yakalamalı, ne zaman yakalamamalı?
- Sık çalışma zamanı hataları
- Görevler
- Kendini sına
- Kayıt noktası
Şimdiye kadar bir hata çıktığında program durdu, sen de kodu düzeltip yeniden çalıştırdın. Ama her hata senin yanlışın değildir: bozuk bir kayıt dosyası, kullanıcının yanlış yazdığı bir ad, ileride internetten gelmeyen bir yanıt. Bu derste programının böyle durumlarda çökmek yerine hatayı yakalamasını ve açık mesajlı kendi hatalarını fırlatmasını sağlamayı, bir şey ters gittiğinde de yığın izini okuyup sorunun kaynağına inmeyi öğreneceksin.
Bütün örnekleri C:\kod\ogren\ders13.js dosyasına yaz; her yeni örnekten önce eski kodu sil ya da satırların başına // koy.
Çalışma zamanı hatası: program yolda düşer
Ders 12'nin son görevinde listeye bozuk bir kayıt eklediğinde programın durduğunu görmüştün. Şimdi aynı sorunu baştan ele alalım. Oyunun kayıt sisteminde her oyuncunun kaydı bir JSON metni; Lux'un kaydı yazılırken oyun kapanmış ve metnin sonu eksik kalmış. Program bütün kayıtları sırayla okuyor:
const kayitlar = [ '{"isim": "Ahri", "seviye": 7}', '{"isim": "Lux", "seviye":', '{"isim": "Jinx", "seviye": 11}',] for (const kayit of kayitlar) { const oyuncu = JSON.parse(kayit) console.log(oyuncu.isim + " seviye " + oyuncu.seviye)}console.log("Bütün kayıtlar okundu")Ahri seviye 7<anonymous_script>:1{"isim": "Lux", "seviye": SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at Object.<anonymous> (C:\kod\ogren\ders13.js:8:25) ... Node.js v24.20.0JSON metinlerinin içinde çift tırnak olduğu için kayıtları tek tırnakla yazdık; iki tırnağın da geçerli olduğunu Ders 1'de görmüştün.
Ahri'nin kaydı okundu, Lux'unkinde program durdu. Jinx'in kaydı hiç okunmadı, son satır da hiç çalışmadı: tek bir bozuk kayıt, sağlam olanları da yanında götürdü.
Bu mesajın biçimini Ders 12'den tanıyorsun: <anonymous_script>:1 ayrıştırılan metnin 1. satırı, altındaki satır da senin kodun değil JSON metninin kendisi. Unexpected end of JSON input, "JSON metni beklenmedik yerde bitti" demek.
Senin dosyandaki yeri at Object.<anonymous> (C:\kod\ogren\ders13.js:8:25) satırı gösteriyor: 8. satır. Ders 1'de bu at satırları için "şimdilik geç" demiştik; bu derste onları okumayı öğreneceksin.
JavaScript neden durur da bozuk kaydı atlayıp devam etmez? Çünkü sana "bu durumda ne yapmamı isterdin?" diye soramaz: kaydı atlamak mı, boş bir oyuncu oluşturmak mı, kullanıcıya haber vermek mi? Bunu bilemez.
Yanlış bir değerle devam etmek daha kötü sonuçlar doğurabilir: altın yanlış hesaplanır, bozuk veri sağlam kaydın üstüne yazılır. Bu yüzden JavaScript en güvenli yolu seçer: durur ve neyin nerede bozulduğunu söyler.
if ile önleyemez miyiz? Ders 12'de ?. ile undefined'a karşı önlem almıştın; o işe yarıyordu, çünkü neyi denetleyeceğin belliydi. Ama bir JSON metninin bozuk olup olmadığını anlamak için bütün tırnakları, parantezleri ve virgülleri denetleyen bir kod yazman gerekirdi, yani JSON.parse'ı baştan yazman. Daha akıllıcası şu: dene, olmazsa ne yapacağını söyle.
try / catch: dene, olmazsa yakala
try { // hata çıkarabilecek satırlar} catch (hata) { // hata çıkınca çalışacak satırlar}try"dene" demek. İçindeki satırlar sırayla çalışır.- Bir satırda hata çıkarsa
try'ın kalan satırları atlanır, programcatch("yakala") bloğuna geçer. catch'in parantezindekihata, yakalanan hatayı tutan bir değişkendir. Adını sen seçersin; internetteki kodlarda çoğunluklaerr,errorya daegöreceksin.- Hata çıkmazsa
catchbloğu hiç çalışmaz. - İki durumda da program,
catch'in kapanan süslü parantezinden sonra yoluna devam eder.
Kayıt programını düzelt: döngünün içindeki iki satırı try'ın içine al, altına bir catch ekle. try / catch döngünün içinde olduğu için her kayıt ayrı ayrı denenir.
const kayitlar = [ '{"isim": "Ahri", "seviye": 7}', '{"isim": "Lux", "seviye":', '{"isim": "Jinx", "seviye": 11}',] for (const kayit of kayitlar) { try { const oyuncu = JSON.parse(kayit) console.log(oyuncu.isim + " seviye " + oyuncu.seviye) } catch (hata) { console.log("Bozuk kayıt atlandı: " + hata.message) }}console.log("Bütün kayıtlar okundu")Ahri seviye 7Bozuk kayıt atlandı: Unexpected end of JSON inputJinx seviye 11Bütün kayıtlar okunduLux'un turunda 9. satırdaki JSON.parse hata fırlattı. 10. satır atlandı, program 12. satıra geçti ve hatanın açıklamasını (hata.message) yazdı. Döngü Jinx'le sürdü, son satır da çalıştı. Program artık çökmüyor; bir kaydı atladığını ve nedenini söylüyor.
try içinde hata çıkınca kalan satırlar atlanır, program catch'ten devam eder. Hata yoksa catch hiç çalışmaz.SyntaxError: Missing catch or finally after tryNe zaman çıkar? try bloğunu yazıp arkasına catch (ya da birazdan göreceğin finally) eklemeyince.
try { const oyuncu = JSON.parse('{"isim": "Ahri"}') console.log(oyuncu.isim)}console.log("Kayıt okundu")C:\kod\ogren\ders13.js:4}^ SyntaxError: Missing catch or finally after try at wrapSafe (node:internal/modules/cjs/loader:1861:18) ... Node.js v24.20.0Nasıl düzelir? try tek başına duramaz; arkasına catch (hata) { ... } ekle. Bu bir yazım hatası olduğu için program hiç başlamadı: ne Ahri ne Kayıt okundu yazıldı.
ReferenceError: hata is not definedNe zaman çıkar? catch (hata)'daki hata'yı catch bloğunun dışında kullanınca.
try { JSON.parse('{"isim": "Lux", "seviye":')} catch (hata) { console.log("Kayıt bozuk")}console.log(hata.message)Kayıt bozukC:\kod\ogren\ders13.js:6console.log(hata.message) ^ ReferenceError: hata is not defined at Object.<anonymous> (C:\kod\ogren\ders13.js:6:13) ... Node.js v24.20.0Nasıl düzelir? hata yalnızca catch'in süslü parantezleri içinde vardır (blok kapsamı, Ders 10). Onu kullanan satırı catch'in içine taşı.
Hata objesi: name, message, stack
catch (hata)'daki hata bir obje (Ders 4). Bir hata çıkınca JavaScript bu objeyi oluşturur, içine hatayla ilgili bilgileri koyar ve catch'e verir. Bazı kaynaklar hata nesnesi der; nesne ile obje aynı şey. İçine bakmak için Lux'un bozuk kaydını tek başına dene:
const kayit = '{"isim": "Lux", "seviye":' try { JSON.parse(kayit)} catch (hata) { console.log("Tür:", hata.name) console.log("Mesaj:", hata.message)}PS C:\kod\ogren> node ders13.jsTür: SyntaxErrorMesaj: Unexpected end of JSON inputconsole.log'a virgülle birden çok şey verince araya boşluğu kendisi koyar (Ders 1).
| Alan | İçinde ne var? | Ne için kullanılır? |
|---|---|---|
name | Hatanın türü: SyntaxError, TypeError, RangeError, Error… | Programda karar vermek: if (hata.name === "SyntaxError") |
message | Ne olduğunu anlatan açıklama | Ekranda göstermek |
stack | Tür, mesaj ve altındaki at … satırları | Hatanın programda geçtiği yolu bulmak |
hata bir obje. Üç alanının her birinin ayrı bir işi var.Ders 1'deki hata anatomisini hatırla: iki noktadan önceki tür aslında name, sonraki açıklama message. Program çökünce Node'un ekrana bastığı tür, mesaj ve at satırları da stack'in ta kendisi:
const kayit = '{"isim": "Lux", "seviye":' try { JSON.parse(kayit)} catch (hata) { console.log(hata.stack)}SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at Object.<anonymous> (C:\kod\ogren\ders13.js:4:10) at Module._compile (node:internal/modules/cjs/loader:1929:14) ...at satırlarındaki yol ve sayılar bilgisayarına ve Node sürümüne göre değişir; çıktının "örnek" olması bundan. console.log(hata) yazarsan Node yine bu metni yazar. Node'un kendi hatalarında bazen bir alan daha olur: code. Ders 10'daki setTimeout hatasının sonunda gördüğün code: 'ERR_INVALID_ARG_TYPE' buydu.
throw: kendi hatanı fırlat
Şimdiye kadar hataları JavaScript fırlattı. Sen de fırlatabilirsin. Neden isteyesin? Bir dükkân fonksiyonuna bak:
function satinAl(esya, altin) { return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }let altin = 200altin = satinAl(kilic, altin)console.log("Kalan altın: " + altin)Kalan altın: -150Paran yetmediği hâlde kılıcı aldın, altının eksiye düştü. Program çökmedi, mesaj da yok; ama sonuç yanlış: bir mantık hatası. Bu, çökmekten daha tehlikelidir: yanlış değer programın içinde dolaşmaya devam eder ve sorun çok sonra, bambaşka bir yerde ortaya çıkar.
satinAl paranın yetmediğini fark edebilir, ama ne yapılacağına karar veremez. Oyuncuya mesaj mı gösterilsin, daha ucuz bir eşya mı önerilsin? Bunu onu çağıran kod bilir. satinAl'ın işi, sorunu yüksek sesle haber vermektir:
function satinAl(esya, altin) { if (altin < esya.fiyat) { throw new Error("Yetersiz altın") } return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }let altin = 200altin = satinAl(kilic, altin)console.log("Kalan altın: " + altin)C:\kod\ogren\ders13.js:3 throw new Error("Yetersiz altın") ^ Error: Yetersiz altın at satinAl (C:\kod\ogren\ders13.js:3:15) at Object.<anonymous> (C:\kod\ogren\ders13.js:10:9) ... Node.js v24.20.0throw "fırlat" demek. new Error("Yetersiz altın") yeni bir hata objesi üretir; parantezin içindeki metin onun message'ı olur. new sözcüğünü Ders 16'da sınıflarla öğreneceksin; şimdilik throw new Error("...") kalıbını olduğu gibi yaz.
Çıktıyı oku: tür Error, mesaj senin yazdığın Yetersiz altın. at satinAl (…:3:15) hatanın satinAl fonksiyonunun içinde, 3. satırda fırlatıldığını; at Object.<anonymous> (…:10:9) da satinAl'ın 10. satırdan çağrıldığını söylüyor. 11. satır hiç çalışmadı: eksi altın bu kez programa sızamadı.
throw da Ders 6'daki return gibi fonksiyonu o satırda bitirir. Farkı şu: return çağırana bir sonuç verir, throw ise bir hata fırlatır. Çağıran bu hatayı try / catch ile yakalayabilir:
function satinAl(esya, altin) { if (altin < esya.fiyat) { throw new Error("Yetersiz altın") } return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }let altin = 200 try { altin = satinAl(kilic, altin) console.log("Kılıç alındı")} catch (hata) { console.log("Alınamadı: " + hata.message)}console.log("Kalan altın: " + altin)Alınamadı: Yetersiz altınKalan altın: 200satinAl hata fırlatınca 12. satırdaki atama hiç yapılmadı, 13. satır da atlandı; altin 200 olarak kaldı. 9. satırda altin'ı 500 yapıp yeniden çalıştırırsan Kılıç alındı ve Kalan altın: 150 yazar.
throw ile metin fırlatmakNe zaman çıkar? throw new Error("...") yerine yalnızca throw "..." yazınca. JavaScript buna izin verir, ama fırlattığın şey bir hata objesi değil, düz bir metin olur.
function satinAl(esya, altin) { if (altin < esya.fiyat) { throw "Yetersiz altın" } return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }satinAl(kilic, 200) C:\kod\ogren\ders13.js:3 throw "Yetersiz altın" ^Yetersiz altın(Use `node --trace-uncaught ...` to show where the exception was thrown) Node.js v24.20.0Hata türü de at satırları da kayboldu: kimin satinAl'ı çağırdığını artık göremiyorsun. Node yalnızca metni yazıyor; nerede fırlatıldığını görmek için de programı node --trace-uncaught ile çalıştırmanı öneriyor. Yakalarsan durum daha da kötü:
function satinAl(esya, altin) { if (altin < esya.fiyat) { throw "Yetersiz altın" } return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }try { satinAl(kilic, 200)} catch (hata) { console.log("Alınamadı: " + hata.message)}Alınamadı: undefinedBir metnin message'ı, name'i, stack'i yoktur; üçü de undefined çıkar.
Nasıl düzelir? Her zaman throw new Error("...") yaz.
ReferenceError: error is not definedNe zaman çıkar? Error'u küçük harfle yazınca.
function satinAl(esya, altin) { if (altin < esya.fiyat) { throw new error("Yetersiz altın") } return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }satinAl(kilic, 200)C:\kod\ogren\ders13.js:3 throw new error("Yetersiz altın") ^ ReferenceError: error is not defined at satinAl (C:\kod\ogren\ders13.js:3:9) at Object.<anonymous> (C:\kod\ogren\ders13.js:9:1) ... Node.js v24.20.0Nasıl düzelir? JavaScript'in hata türleri büyük harfle başlar: Error, TypeError, RangeError. error diye bir şey yok; mesaj da tam olarak bunu söylüyor.
finally: her durumda çalışan temizlik
Bazı işler hata çıksa da çıkmasa da yapılmalıdır: yükleme ekranını kapatmak, "meşgul" işaretini kaldırmak, ileride açacağın bir dosyayı kapatmak. Bunun için catch'ten sonra bir blok daha yazabilirsin: finally ("sonunda").
function kayitYukle(kayit) { console.log("Yükleme ekranı açıldı") try { const oyuncu = JSON.parse(kayit) console.log("Hoş geldin " + oyuncu.isim) } catch (hata) { console.log("Kayıt bozuk: " + hata.message) } finally { console.log("Yükleme ekranı kapandı") }} kayitYukle('{"isim": "Ahri", "seviye": 7}')kayitYukle('{"isim": "Lux", "seviye":')Yükleme ekranı açıldıHoş geldin AhriYükleme ekranı kapandıYükleme ekranı açıldıKayıt bozuk: Unexpected end of JSON inputYükleme ekranı kapandıSağlam kayıtta sıra try → finally, bozuk kayıtta try → catch → finally oldu. İki durumda da yükleme ekranı kapandı.
Bu örnekte kapanış satırını finally yerine try / catch'in altına yazsan da aynı sonucu alırdın. finally'nin asıl gücü, fonksiyon yarıda bittiğinde ortaya çıkar: try ya da catch içindeki bir return ile ya da yakalanmayan bir hatayla. Önce return:
function seviyeOku(kayit) { try { return JSON.parse(kayit).seviye } catch (hata) { return 1 } finally { console.log("Yükleme ekranı kapandı") }} console.log(seviyeOku('{"isim": "Ahri", "seviye": 7}'))console.log(seviyeOku('{"isim": "Lux", "seviye":'))Yükleme ekranı kapandı7Yükleme ekranı kapandı1return fonksiyonu bitirir, ama fonksiyon sonucu geri vermeden hemen önce finally çalışır; mesajın, döndürülen sayıdan önce yazılması bundan. Kapanış satırını return'den sonraya yazsaydın hiç çalışmazdı (Ders 6).
catch olmadan finally
try'ın arkasına yalnızca finally de yazabilirsin. O zaman hata yakalanmaz: finally temizliğini yapar, ardından hata, fonksiyonu çağıran koda gider. Hatayı fonksiyonun içinde değil, onu çağıran yerde karşılamak istediğinde bu kalıp işine yarar:
function seviyeOku(kayit) { console.log("Yükleme ekranı açıldı") try { return JSON.parse(kayit).seviye } finally { console.log("Yükleme ekranı kapandı") }} try { console.log(seviyeOku('{"isim": "Lux", "seviye":'))} catch (hata) { console.log("Kayıt okunamadı: " + hata.message)}Yükleme ekranı açıldıYükleme ekranı kapandıKayıt okunamadı: Unexpected end of JSON inputseviyeOku bu kez hatayı yakalamadı. 4. satırdaki JSON.parse hata fırlatınca önce finally çalıştı ve yükleme ekranı kapandı; hata ancak ondan sonra dışarıdaki try'a ulaştı. Görev 2'de bu kalıbı kullanacaksın.
| Ne oldu? | try | catch | finally |
|---|---|---|---|
| Hata yok | sonuna kadar çalışır | çalışmaz | çalışır |
| Hata çıktı | hatalı satırda kesilir | çalışır | çalışır |
try içinde return | return'e kadar çalışır | çalışmaz | fonksiyon bitmeden çalışır |
Hata çıktı, catch yok | hatalı satırda kesilir | yok | çalışır, sonra hata çağırana gider |
Yığın izini okumak
Hata, birbirini çağıran birkaç fonksiyonun derininde çıkınca mesajdaki at satırları en iyi yol göstericin olur. Bir TFT dükkânı düşün: her birimin bir maliyeti var; takımın değerini bulmak için her birimin maliyeti yıldız sayısıyla çarpılıp sonuçlar toplanıyor.
const dukkan = [ { ad: "Ahri", maliyet: 4 }, { ad: "Garen", maliyet: 1 }, { ad: "Jinx", maliyet: 3 },] function birimBul(ad) { return dukkan.find((birim) => birim.ad === ad)} function birimDegeri(ad, yildiz) { const birim = birimBul(ad) return birim.maliyet * yildiz} function takimDegeri(takim) { let toplam = 0 for (const uye of takim) { toplam = toplam + birimDegeri(uye.ad, uye.yildiz) } return toplam} const takim = [ { ad: "Ahri", yildiz: 2 }, { ad: "Jinx", yildiz: 1 }, { ad: "Yasuo", yildiz: 3 },]console.log("Takım değeri: " + takimDegeri(takim))C:\kod\ogren\ders13.js:13 return birim.maliyet * yildiz ^ TypeError: Cannot read properties of undefined (reading 'maliyet') at birimDegeri (C:\kod\ogren\ders13.js:13:18) at takimDegeri (C:\kod\ogren\ders13.js:19:27) at Object.<anonymous> (C:\kod\ogren\ders13.js:29:32) at Module._compile (node:internal/modules/cjs/loader:1929:14) ... Node.js v24.20.0Programda fonksiyonlar birbirini çağırıyor: ana program takimDegeri'ni, takimDegeri her üye için birimDegeri'ni, birimDegeri de birimBul'u. Hata mesajını yukarıdan aşağı, satır satır oku:
| Satır | Ne söylüyor? |
|---|---|
C:\kod\ogren\ders13.js:13 ve ^ | Patladığı yer: 13. satır, maliyet'in başı. |
TypeError: … (reading 'maliyet') | Ne oldu: undefined'ın içinden maliyet okunmak istendi. Demek ki birim undefined. |
at birimDegeri (…ders13.js:13:18) | Hata birimDegeri'nin içinde, 13. satırın 18. karakterinde çıktı. |
at takimDegeri (…ders13.js:19:27) | birimDegeri'ni takimDegeri çağırmıştı, 19. satırdan. |
at Object.<anonymous> (…ders13.js:29:32) | takimDegeri'ni ana program çağırmıştı, 29. satırdan. Object.<anonymous>, dosyanın hiçbir fonksiyonun içinde olmayan kısmı demek. |
at Module._compile (node:internal/…) ve sonrası | Node'un kendi iç kodu: dosyanı yükleyip çalıştıran kısım. Okumayı burada bırak. |
Dosya adından sonraki iki sayı satır ve sütundur: 13:18, "13. satır, 18. karakter". VS Code'un sağ alt köşesindeki Ln 13, Col 18 göstergesi de aynı şeyi söyler.
at satırı bir kat: en üstte hatanın çıktığı fonksiyon, altında onu çağıranlar.Şekilde bir şey dikkatini çekmeli: birimBul yığın izinde yok. Çünkü o, işini bitirip undefined döndürmüş ve yığından çıkmıştı. Yığın izi yalnızca hata anında hâlâ bitmemiş, sonuç bekleyen fonksiyonları gösterir.
Patladığı yer, sebebin olduğu yer değil
Yığın izi hatanın nerede patladığını söyler; sebebi her zaman orada değildir. 13. satırda birim undefined. Neden? birim 12. satırda birimBul(ad)'dan geldi.
birimBul neden bulamadı? ad, 19. satırda uye.ad'dan geldi. O üye de 27. satırdaki { ad: "Yasuo", yildiz: 3 }: dükkânda Yasuo yok. Değerin geldiği yolu geriye doğru izledin ve sebebi buldun.
- Ne oldu? Tür ve mesaj satırını oku (
TypeError: … (reading 'maliyet')). - Nerede patladı? İlk
atsatırına bak: fonksiyonun adı vesatır:sütun. Bu satırJSON.parse (<anonymous>)ya danode:gibi JavaScript'in veya Node'un kendi kodunu gösteriyorsa, kendi dosyanın adının geçtiği ilk satırı bul. - Kim çağırdı? Kendi dosyanın
atsatırlarını aşağı doğru izle; her satır, üstündekini çağıran yerdir. - Nerede durmalı? İçinde
node:internalgeçen ilkatsatırında. - Sebep nerede? Patlayan değerin nereden geldiğini bu satırlarda geriye doğru takip et.
Girdi doğrulama: erken ve açık hata
Yığın izi örneğindeki asıl sorun şuydu: hata, sebebinden birkaç adım ötede ve şifreli bir mesajla patladı. Cannot read properties of undefined (reading 'maliyet') yazısından "dükkânda Yasuo yok" sonucuna varmak için bütün yolu geri izlemen gerekti. Hata, sorunun doğduğu yerde ve açık bir mesajla fırlatılsaydı? birimBul'u değiştir:
const dukkan = [ { ad: "Ahri", maliyet: 4 }, { ad: "Garen", maliyet: 1 }, { ad: "Jinx", maliyet: 3 },] function birimBul(ad) { const birim = dukkan.find((b) => b.ad === ad) if (birim === undefined) { throw new Error(`Dükkânda "${ad}" adlı birim yok`) } return birim} function birimDegeri(ad, yildiz) { const birim = birimBul(ad) return birim.maliyet * yildiz} function takimDegeri(takim) { let toplam = 0 for (const uye of takim) { toplam = toplam + birimDegeri(uye.ad, uye.yildiz) } return toplam} const takim = [ { ad: "Ahri", yildiz: 2 }, { ad: "Jinx", yildiz: 1 }, { ad: "Yasuo", yildiz: 3 },]console.log("Takım değeri: " + takimDegeri(takim))C:\kod\ogren\ders13.js:10 throw new Error(`Dükkânda "${ad}" adlı birim yok`) ^ Error: Dükkânda "Yasuo" adlı birim yok at birimBul (C:\kod\ogren\ders13.js:10:15) at birimDegeri (C:\kod\ogren\ders13.js:16:19) at takimDegeri (C:\kod\ogren\ders13.js:23:27) at Object.<anonymous> (C:\kod\ogren\ders13.js:33:32) ... Node.js v24.20.0Veri aynı, ama bu kez mesaj sebebi doğrudan söylüyor ve hata, sorunun doğduğu birimBul'da fırlatılıyor. Buna girdi doğrulama denir: fonksiyon kendisine gelen değerleri işe başlamadan denetler; uygun değilse hemen ve açık bir hatayla durur. Mesajı Ders 9'daki şablon metinle (`…${ad}…`) yazdık, böylece gelen değer de mesajın içinde.
- Doğrulamasız: mesaj
… (reading 'maliyet'), patladığı yerbirimDegeri(13. satır). Sebebi bulmak için yolu geriye izlemek gerekir. - Doğrulamalı: mesaj
Dükkânda "Yasuo" adlı birim yok, patladığı yerbirimBul(10. satır), yani sorunun doğduğu yer. Mesajı okumak yeter.
Bu tür denetimleri kapıdaki bekçi gibi düşün: geçersiz değeri içeri almaz, fonksiyonun geri kalanı da gelen değerin doğru olduğuna güvenerek yazılır. Ders 6'daki erken çıkışa benziyor; fark, bekçinin return yerine throw kullanması: sessizce dönmez, ne olduğunu söyleyerek durur.
Yıldız için de bir bekçi ekle; bizim dükkânda bir birim 1, 2 ya da 3 yıldızlı olabilir. takimDegeri'ni ve son satırları sil, yerine dört deneme yapan bir döngü yaz:
const dukkan = [ { ad: "Ahri", maliyet: 4 }, { ad: "Garen", maliyet: 1 }, { ad: "Jinx", maliyet: 3 },] function birimBul(ad) { const birim = dukkan.find((b) => b.ad === ad) if (birim === undefined) { throw new Error(`Dükkânda "${ad}" adlı birim yok`) } return birim} function birimDegeri(ad, yildiz) { if (![1, 2, 3].includes(yildiz)) { const mesaj = `Yıldız 1-3 olmalı, gelen: ${yildiz}` throw new RangeError(mesaj) } const birim = birimBul(ad) return birim.maliyet * yildiz} const denemeler = [ { ad: "Jinx", yildiz: 2 }, { ad: "Jinx", yildiz: 4 }, { ad: "Jinx" }, { ad: "Teemo", yildiz: 1 },] for (const deneme of denemeler) { try { const deger = birimDegeri(deneme.ad, deneme.yildiz) console.log(deneme.ad + ": " + deger + " altın") } catch (hata) { console.log(hata.name + ": " + hata.message) }}Jinx: 6 altınRangeError: Yıldız 1-3 olmalı, gelen: 4RangeError: Yıldız 1-3 olmalı, gelen: undefinedError: Dükkânda "Teemo" adlı birim yokDört denemenin hiçbiri programı durdurmadı; geçersiz olanlar ne olduğunu açıkça söyledi. gelen: undefined satırına bak: yıldızı yazmayı unuttuğunu mesajın kendisi söylüyor. yildiz < 1 || yildiz > 3 diye denetleseydin undefined bu bekçiden geçerdi (undefined < 1 de undefined > 3 de false'tur) ve sonuç sessizce NaN olurdu. İzin verilen değerleri tek tek saymak ([1, 2, 3].includes(yildiz)) daha güvenli.
Bir yenilik daha: RangeError. Hata türünü sen de seçebilirsin. Değer izin verilen değerlerden biri değilse ya da aralığın dışındaysa new RangeError("..."), türü yanlışsa (sayı yerine metin gelmesi gibi) new TypeError("...") yazarsın; kullanımları new Error ile aynı. catch tarafında hata.name bu türü gösterir.
Teemo'nun hatası birimBul'da fırlatıldı ama onu ana programdaki döngü yakaladı. Aradaki birimDegeri'nde try olmadığı için hata oradan geçip gitti:
catch bulana kadar çağıranlara geri gider.İyi bir hata mesajı üç şey söyler: ne bekleniyordu, ne geldi, hangi şeyde. "Geçersiz değer" ya da "Hata!" hiçbirini söylemez; Yıldız 1-3 olmalı, gelen: 4 üçünü de söyler. Mesaj uzunsa bekçideki gibi önce bir mesaj değişkenine yazıp sonra fırlatabilirsin.
Erken denetle
Erken, işe başlamadan demek. Denetimi işin ortasına koyarsan hata çıktığında iş yarım kalır:
const ahri = { altin: 200, envanter: [] } function esyaAl(oyuncu, esya) { oyuncu.altin = oyuncu.altin - esya.fiyat if (oyuncu.altin < 0) { throw new Error("Yetersiz altın") } oyuncu.envanter.push(esya.ad)} try { esyaAl(ahri, { ad: "Uzun Kılıç", fiyat: 350 })} catch (hata) { console.log("Alınamadı: " + hata.message)}console.log(ahri)Alınamadı: Yetersiz altın{ altin: -150, envanter: [] }Alışveriş olmadı ama altın 350 azaldı, çünkü denetim altın düşüldükten sonra yapıldı. Denetimi en başa al:
const ahri = { altin: 200, envanter: [] } function esyaAl(oyuncu, esya) { if (oyuncu.altin < esya.fiyat) { throw new Error("Yetersiz altın") } oyuncu.altin = oyuncu.altin - esya.fiyat oyuncu.envanter.push(esya.ad)} try { esyaAl(ahri, { ad: "Uzun Kılıç", fiyat: 350 })} catch (hata) { console.log("Alınamadı: " + hata.message)}console.log(ahri)Alınamadı: Yetersiz altın{ altin: 200, envanter: [] }Hata bu kez hiçbir şey değişmeden fırlatıldı; Ahri'nin altını yerinde.
Ne zaman yakalamalı, ne zaman yakalamamalı?
catch her hatayı yakalar; beklemediklerini de. Bu bir tuzaktır:
function satinAl(esya, altin) { if (altin < esya.fiyat) { throw new Error("Yetersiz altın") } return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }let altin = 500 try { altin = satinAl(kilic, altn) console.log("Kılıç alındı")} catch (hata) { console.log("Paran yetmedi, kılıç alınamadı")}console.log("Kalan altın: " + altin)Paran yetmedi, kılıç alınamadıKalan altın: 500500 altınla 350'lik kılıç alınamadı mı? Program "paran yetmedi" diyor ama yanılıyor: 12. satırda altin yerine altn yazılmış. Asıl hata ReferenceError: altn is not defined; catch onu da yakaladı ve üstüne yanlış bir açıklama yapıştırdı. Böyle bir hatayı saatlerce yanlış yerde ararsın.
Çözüm: yalnızca ne yapacağını bildiğin hatayı işle, gerisini throw hata ile yeniden fırlat:
function satinAl(esya, altin) { if (altin < esya.fiyat) { throw new Error("Yetersiz altın") } return altin - esya.fiyat} const kilic = { ad: "Uzun Kılıç", fiyat: 350 }let altin = 500 try { altin = satinAl(kilic, altn) console.log("Kılıç alındı")} catch (hata) { if (hata.message === "Yetersiz altın") { console.log("Paran yetmedi, kılıç alınamadı") } else { throw hata }}console.log("Kalan altın: " + altin)C:\kod\ogren\ders13.js:18 throw hata ^ ReferenceError: altn is not defined at Object.<anonymous> (C:\kod\ogren\ders13.js:12:28) ... Node.js v24.20.0Yanlış yazılan ad artık gizlenmiyor: program duruyor ve gerçek mesajı gösteriyor. En üst satır 18'i (throw hata) gösteriyor, çünkü hata en son orada fırlatıldı. Ama ilk at satırı (12:28) hatanın doğduğu yeri söylüyor: altn. Onu düzeltirsen program Kılıç alındı ve Kalan altın: 150 yazar.
| Durum | Yakala mı? | Ne yap? |
|---|---|---|
| Dışarıdan gelen veri bozuk olabilir (kayıt, kullanıcı girdisi) | Evet | Haber ver; kaydı atla ya da varsayılan değer kullan |
Kendi fırlattığın, karşılığını bildiğin hata (Yetersiz altın) | Evet, kararı veren yerde | Mesajı göster, başka seçenek sun |
Kodundaki hata: yanlış yazılmış ad, undefined'dan okuma | Hayır | Program dursun; yığın izini oku, kodu düzelt |
| Ne yapacağını bilmediğin hata | Hayır | Yakaladıysan throw hata ile geri fırlat |
Üç alışkanlık:
catch'i asla boş bırakma. En azındanhata.message'ı yaz.try'ı dar tut. Yalnızca hata çıkarabilecek satırları içine al. Bütün programı tek birtry'a sarmak, bütün hataları tek bircatch'in altına süpürmek demektir.ifile önleyebildiğinitryile yakalama. Boş bir dizi içinif (takim.length === 0)yeter.try, önceden denetleyemediğin şeyler içindir: bozuk JSON gibi.
catch: hata kayboldu, sonuç yanlışNe zaman çıkar? catch bloğunu boş bırakınca. Program çökmez, hiçbir şey söylemez; ama sonuç sessizce yanlış olur.
const kayitlar = [ '{"isim": "Ahri", "altin": 450}', '{"isim": "Lux", "altin":', '{"isim": "Jinx", "altin": 300}',] let toplam = 0for (const kayit of kayitlar) { try { toplam = toplam + JSON.parse(kayit).altin } catch (hata) { }}console.log("Takımın toplam altını: " + toplam)Takımın toplam altını: 750Takımda üç oyuncu var, ama toplam yalnızca ikisinin altınını içeriyor. Lux'un kaydı bozuk ve bunu kimse bilmiyor; bu sayıya güvenip karar verirsen yanılırsın.
Nasıl düzelir? catch'in içinde en azından ne olduğunu yaz: console.log("Bozuk kayıt atlandı: " + hata.message). Eksik bir şey olduğunu kullanıcı da sen de bilmelisin.
try bloğunun dışında kalan hataNe zaman çıkar? Hata çıkarabilecek satırı try'ın dışına yazınca. try / catch yalnızca kendi süslü parantezleri içindeki satırları korur.
const kayit = '{"isim": "Lux", "seviye":'const oyuncu = JSON.parse(kayit) try { console.log(oyuncu.isim + " seviye " + oyuncu.seviye)} catch (hata) { console.log("Bozuk kayıt: " + hata.message)}<anonymous_script>:1{"isim": "Lux", "seviye": SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at Object.<anonymous> (C:\kod\ogren\ders13.js:2:21) ... Node.js v24.20.0Nasıl düzelir? JSON.parse satırını try'ın içine al. Bu hata çoğunlukla şöyle yapılır: try'ın içinde const ile oluşturulan değişken dışarıda görünmez (blok kapsamı), sen de onu dışarıda kullanabilmek için riskli satırı dışarı çıkarırsın. Doğrusu, değişkeni kullanan satırları da try'ın içine almak.
catch yanlış yazılan adı yuttu, yanlış mesaj verdiNe zaman çıkar? try'ın içinde bir adı yanlış yazınca (altin yerine altn). JavaScript ReferenceError fırlatır, catch ise hangi hatayı yakaladığına bakmadan kendi cümlesini yazar. Program çökmez ama yanlış şey söyler.
Bu bölümün başındaki örnekte 500 altınla 350'lik kılıç, "paran yetmedi" denerek alınmadı.
Nasıl düzelir? catch'te en azından hata.message'ı yazdır; daha iyisi, yalnızca beklediğin hatayı işleyip gerisini throw hata ile geri fırlat. O zaman gerçek mesaj ortaya çıkar: ReferenceError: altn is not defined.
Sık çalışma zamanı hataları
Hata türlerini Ders 1'den tanıyorsun: SyntaxError yazım, ReferenceError tanınmayan ad, TypeError yanlış kullanım. Bu derste iki tür daha gördün: kendi fırlattığın genel Error ve aralık dışı değerler için RangeError. JavaScript de RangeError fırlatır:
RangeError: toFixed() digits argument must be between 0 and 100Ne zaman çıkar? Ders 9'daki toFixed'e 0 ile 100 arasında olmayan bir basamak sayısı verince.
const kda = 3.14159console.log(kda.toFixed(2))console.log(kda.toFixed(101))3.14C:\kod\ogren\ders13.js:3console.log(kda.toFixed(101)) ^ RangeError: toFixed() digits argument must be between 0 and 100 at Number.toFixed (<anonymous>) at Object.<anonymous> (C:\kod\ogren\ders13.js:3:17) ... Node.js v24.20.0Nasıl düzelir? Basamak sayısı 0 ile 100 arasında olmalı. Sayı bir hesaptan ya da kullanıcıdan geliyorsa toFixed'den önce doğrula. at Number.toFixed (<anonymous>) satırı, hatanın JavaScript'in kendi toFixed'inin içinde çıktığını söylüyor; senin satırın hemen altındaki: ders13.js:3.
RangeError: Maximum call stack size exceededNe zaman çıkar? Bir fonksiyon kendini durmadan çağırınca. Her çağrı yığına bir kat ekler; çağrı yığınının bir üst sınırı vardır ve dolunca program durur.
function seviyeAtla(seviye) { return seviyeAtla(seviye + 1)} console.log(seviyeAtla(1))C:\kod\ogren\ders13.js:2 return seviyeAtla(seviye + 1) ^ RangeError: Maximum call stack size exceeded at seviyeAtla (C:\kod\ogren\ders13.js:2:5) at seviyeAtla (C:\kod\ogren\ders13.js:2:12) at seviyeAtla (C:\kod\ogren\ders13.js:2:12) at seviyeAtla (C:\kod\ogren\ders13.js:2:12) ... Node.js v24.20.0Yığın izinde aynı fonksiyonun alt alta tekrar etmesi bu hatanın imzasıdır (Node yalnızca ilk 10 satırı gösterir). Bunu bilerek yapmanın da bir adı var, özyineleme (recursion), ama buradaki konumuz o değil.
Nasıl düzelir? Tekrar eden satıra git (ders13.js:2) ve çağrının neden hiç bitmediğine bak. Çoğu zaman yanlış fonksiyonu çağırmışsındır: başka bir fonksiyonu çağıracakken fonksiyonun kendi adını yazmak gibi.
Programlarında en sık karşılaşacağın çalışma zamanı hataları ve anlamları:
TypeError: Cannot read properties of undefined (reading 'hp'):undefined'ın içinden alan okunmak istendi.findbir şey bulamayınca, olmayan bir alanın içine bakınca ya da dizinin dışındaki bir sırayı okuyunca çıkar (Ders 12).TypeError: Cannot set properties of undefined (setting 'hp'): aynısı, bu kez alana değer yazarken.TypeError: takim.map is not a function: fonksiyon gibi çağırdığın şey fonksiyon değil. Dizi sandığın şey obje ya da metindir, ya da metodun adını yanlış yazmışsındır.TypeError: Reduce of empty array with no initial value: boş bir dizide başlangıç değeri verilmemişreduce(Ders 11). İkinci argümanı (başlangıç değerini) ver.TypeError: Cannot convert undefined or null to object: obje beklenen yereundefinedya danullverildi; örneğinObject.keys(undefined).TypeError: ahri.esyalar is not iterable:for...ofya da...ile gezmek istediğin şey bir dizi değil; çoğu zamanundefinedya da bir obje (Ders 12).TypeError: Cannot destructure property 'hp' of 'ahri.istatistik' as it is undefined.: parçalamaya çalıştığın değerundefined; tırnak içinde nereden geldiği yazar (Ders 12).SyntaxError: Unexpected end of JSON input:JSON.parse'a boş ya da yarım bir metin verildi.
try / catch dosyadaki yazım hatasını yakalamazNe zaman çıkar? Dosyandaki bir yazım hatasını (SyntaxError) try ile yakalamayı umduğunda.
console.log("Program başladı")try { let altin = 300 altin = altin -} catch (hata) { console.log("Yakalandı: " + hata.message)}C:\kod\ogren\ders13.js:5} catch (hata) {^ SyntaxError: Unexpected token '}' at wrapSafe (node:internal/modules/cjs/loader:1861:18) ... Node.js v24.20.0En üstteki Program başladı satırı bile çalışmadı. Node dosyayı çalıştırmadan önce okur; yazım hatası bulunca hiçbir satırı çalıştırmaz, try daha devreye bile girmemiştir. Mesaj 5. satırı gösteriyor, çünkü JavaScript 4. satırdaki - işaretinden sonra bir değer beklerken karşısına } çıktı.
Nasıl düzelir? Yazım hatası yakalanmaz, düzeltilir: 4. satırdaki yarım işlemi tamamla. try / catch yalnızca çalışma zamanı hatalarını yakalar.
Görevler
Her görevi ders13.js dosyasında yap; önceki kodu sil ya da yorum satırına çevir. Önce kendin dene, takılırsan ipuçlarını sırayla aç. Çözüme en son bak.
Mana yetiyor mu?
yetenekKullan(mana, bedel) adında bir fonksiyon yaz. Mana yetiyorsa kalan manayı döndürsün, yetmiyorsa "Yetersiz mana" mesajlı bir hata fırlatsın. Sonra 100 manayla başla ve sırasıyla 30, 50, 40 ve 20 manalık dört yetenek kullan. Mana yetmeyen yetenek programı durdurmasın, sıradakine geçilsin. Çıktı:
30 mana: kalan 7050 mana: kalan 2040 mana: Yetersiz mana20 mana: kalan 0İpucu 1
Fonksiyonun içi satinAl'a benziyor: önce if ile denetle; yetmiyorsa throw new Error("Yetersiz mana"), yetiyorsa return.
İpucu 2
Bedelleri bir diziye koy ve for...of ile gez. try / catch döngünün içinde olmalı; dışında olursa ilk hatada bütün döngü biter.
İpucu 3
Son satır bir sınır denemesi: 20 mana varken 20 manalık yetenek kullanılabilmeli. Orada Yetersiz mana görüyorsan karşılaştırma işaretine bak.
Çözümü göster
function yetenekKullan(mana, bedel) { if (mana < bedel) { throw new Error("Yetersiz mana") } return mana - bedel} let mana = 100const bedeller = [30, 50, 40, 20] for (const bedel of bedeller) { try { mana = yetenekKullan(mana, bedel) console.log(bedel + " mana: kalan " + mana) } catch (hata) { console.log(bedel + " mana: " + hata.message) }}30 mana: kalan 7050 mana: kalan 2040 mana: Yetersiz mana20 mana: kalan 0Sınır denemesi: mana < bedel yerine mana <= bedel yazsaydın son satırda da Yetersiz mana çıkardı, çünkü 20, 20'ye eşit. Mana tam yetiyorsa yetenek kullanılabilmeli. Hata çıkan turda mana = … ataması yapılmadığı için mana 20'de kaldı.
Hextech sandığı
Hextech sandığı açılırken ekranda "açılıyor" yazısı görünüyor; bunu aciliyor değişkeni tutuyor. Elindeki anahtar sayısını alan sandikAc(anahtar) fonksiyonunu yaz:
- Başta
aciliyor'utrueyapsın. - Anahtar yoksa (
anahtar < 1)"Anahtarın yok"mesajlı bir hata fırlatsın; varsa bir anahtar harcayıp kalan anahtar sayısını döndürsün. - Hata çıksa da çıkmasa da en sonunda
aciliyor'ufalseyapsın. Hatayı yakalamasın; onu aşağıdaki deneme kodu yakalayacak.
Deneme kodu, fonksiyonun altına:
let anahtar = 2for (let i = 1; i <= 3; i++) { try { anahtar = sandikAc(anahtar) console.log(i + ". açıldı, kalan: " + anahtar) } catch (hata) { console.log(i + ". açılmadı: " + hata.message) } console.log("açılıyor mu: " + aciliyor)}Beklenen çıktı:
1. açıldı, kalan: 1açılıyor mu: false2. açıldı, kalan: 0açılıyor mu: false3. açılmadı: Anahtarın yokaçılıyor mu: falseİpucu 1
let aciliyor = false satırı fonksiyonun dışında, dosyanın en üstünde olmalı; hem fonksiyon hem deneme kodu onu görmeli.
İpucu 2
Fonksiyonun içine catch'siz bir try / finally yaz: throw ve return try'ın içinde, aciliyor = false ise finally'de.
Çözümü göster
let aciliyor = false function sandikAc(anahtar) { aciliyor = true try { if (anahtar < 1) { throw new Error("Anahtarın yok") } return anahtar - 1 } finally { aciliyor = false }} let anahtar = 2for (let i = 1; i <= 3; i++) { try { anahtar = sandikAc(anahtar) console.log(i + ". açıldı, kalan: " + anahtar) } catch (hata) { console.log(i + ". açılmadı: " + hata.message) } console.log("açılıyor mu: " + aciliyor)}1. açıldı, kalan: 1açılıyor mu: false2. açıldı, kalan: 0açılıyor mu: false3. açılmadı: Anahtarın yokaçılıyor mu: falseaciliyor = false satırını finally yerine return'den hemen önce yazsaydın ilk iki deneme yine doğru görünürdü. Ama üçüncü denemede throw o satırı atlar ve son satır açılıyor mu: true olurdu: ekranda sonsuza kadar "açılıyor" yazısı kalırdı. Son anahtarın da harcanabilmesi (2. deneme) bir sınır denemesi.
Yığın izi dedektifi
Bu program her oyuncunun KDA'sını (öldürme ve asist toplamının ölüm sayısına oranı; hiç ölmeyenin ölümü 1 sayılıyor) yazmalıydı, ama çöküyor. Kodu değiştirmeden önce çalıştır ve yalnızca hata mesajına bakarak şu soruları cevapla:
const istatistik = [ { isim: "Ahri", kill: 8, olum: 2, asist: 7 }, { isim: "Garen", kill: 3, olum: 0, asist: 4 }, { isim: "Lux", kill: 2, olum: 5, asist: 12 },] function oyuncuBul(isim) { return istatistik.find((o) => o.isim === isim)} function kdaHesapla(isim) { const oyuncu = oyuncuBul(isim) const bolen = Math.max(oyuncu.olum, 1) return ((oyuncu.kill + oyuncu.asist) / bolen).toFixed(1)} function raporYaz(isimler) { for (const isim of isimler) { console.log(isim + " KDA: " + kdaHesapla(isim)) }} raporYaz(["Ahri", "Garen", "lux"])Ahri KDA: 7.5Garen KDA: 7.0C:\kod\ogren\ders13.js:13 const bolen = Math.max(oyuncu.olum, 1) ^ TypeError: Cannot read properties of undefined (reading 'olum') at kdaHesapla (C:\kod\ogren\ders13.js:13:35) at raporYaz (C:\kod\ogren\ders13.js:19:39) at Object.<anonymous> (C:\kod\ogren\ders13.js:23:1) ... Node.js v24.20.0- Hata hangi fonksiyonda, hangi satırda patladı?
- O fonksiyonu kim, hangi satırdan çağırdı? Onu da kim çağırdı?
- Patlayan değer neden
undefined? Asıl sebep hangi satırda?
Sonra iki şey yap: oyuncuBul'a bir bekçi ekle ki aradığı oyuncuyu bulamazsa Listede "lux" adlı oyuncu yok gibi açık bir hata fırlatsın. Ardından asıl sebebi düzelt. Beklenen çıktı:
Ahri KDA: 7.5Garen KDA: 7.0Lux KDA: 2.8İpucu 1
İlk at satırı patladığı yeri, ikincisi onu çağıranı, üçüncüsü de onu çağıranı gösterir. Object.<anonymous> ana program demek.
İpucu 2
oyuncu 12. satırda oyuncuBul(isim)'den geliyor; isim de 23. satırdaki dizinin elemanlarından biri. find'a verilen fonksiyon adları === ile karşılaştırıyor ve === büyük-küçük harfe duyarlı.
Çözümü göster
kdaHesapla'da, 13. satırda:oyuncu.olumokunurken.kdaHesapla'yıraporYaz19. satırdan çağırdı;raporYaz'ı da ana program 23. satırdan.oyuncuBul("lux")kimseyi bulamadı veundefineddöndürdü, çünkü listede"Lux"büyük harfle yazılı. Asıl sebep 23. satırdaki"lux".
Bekçili ve düzeltilmiş hâli:
const istatistik = [ { isim: "Ahri", kill: 8, olum: 2, asist: 7 }, { isim: "Garen", kill: 3, olum: 0, asist: 4 }, { isim: "Lux", kill: 2, olum: 5, asist: 12 },] function oyuncuBul(isim) { const oyuncu = istatistik.find((o) => o.isim === isim) if (oyuncu === undefined) { throw new Error(`Listede "${isim}" adlı oyuncu yok`) } return oyuncu} function kdaHesapla(isim) { const oyuncu = oyuncuBul(isim) const bolen = Math.max(oyuncu.olum, 1) return ((oyuncu.kill + oyuncu.asist) / bolen).toFixed(1)} function raporYaz(isimler) { for (const isim of isimler) { console.log(isim + " KDA: " + kdaHesapla(isim)) }} raporYaz(["Ahri", "Garen", "Lux"])Ahri KDA: 7.5Garen KDA: 7.0Lux KDA: 2.8Bekçiyi ekleyip adı düzeltmeden çalıştırırsan mesaj sebebi doğrudan söyler: Error: Listede "lux" adlı oyuncu yok, yığın izinin en üstünde de at oyuncuBul. Garen'in satırı ayrıca bir sınır denemesi: 0 ölümde Math.max(0, 1) 1 verir, böylece sıfıra bölünmez ve Infinity çıkmaz.
Takım kurma bekçisi
takimKur(isimler) fonksiyonu şampiyon adlarından oluşan bir dizi alsın ve şu durumlarda açık bir hata fırlatsın:
- Dizi boşsa:
Takım boş olamaz - 5'ten fazla ad varsa,
RangeErrortüründe:En fazla 5 şampiyon, gelen: 6 - Bir ad boşsa ya da yalnızca boşluktan oluşuyorsa:
2. sıradaki ad boş(sıra 1'den başlasın) - Aynı ad iki kez varsa:
"Ahri" iki kez seçilmiş
Sorun yoksa Takım hazır: 3 şampiyon gibi bir metin döndürsün. Şu altı diziyi bir döngüde dene; hata çıkarsa hata.name ve hata.message'ı yazdır:
const denemeler = [ ["Ahri", "Garen", "Jinx"], [], ["Ahri", "Lux", "Garen", "Jinx", "Teemo", "Annie"], ["Ahri", " ", "Lux"], ["Ahri", "Lux", "Ahri"], ["Ahri", "Lux", "Garen", "Jinx", "Teemo"],]Beklenen çıktı:
Takım hazır: 3 şampiyonError: Takım boş olamazRangeError: En fazla 5 şampiyon, gelen: 6Error: 2. sıradaki ad boşError: "Ahri" iki kez seçilmişTakım hazır: 5 şampiyonİpucu 1
Önce dizinin bütününe bak (boş mu, 5'ten uzun mu), sonra adları tek tek gez. Sıra numarası gerektiği için for...of yerine sayaçlı for döngüsü (Ders 5) daha uygun.
İpucu 2
Boşluktan oluşan adı yakalamak için Ders 9'daki trim: isim.trim() === "". Aynı adın ikinci kez geçtiğini anlamak için isimler.indexOf(isim): bu, adın ilk geçtiği sırayı verir. O sıra şu anki i değilse ad daha önce de geçmiştir.
Çözümü göster
function takimKur(isimler) { const sayi = isimler.length if (sayi === 0) { throw new Error("Takım boş olamaz") } if (sayi > 5) { const mesaj = `En fazla 5 şampiyon, gelen: ${sayi}` throw new RangeError(mesaj) } for (let i = 0; i < sayi; i++) { const isim = isimler[i] if (isim.trim() === "") { throw new Error(`${i + 1}. sıradaki ad boş`) } if (isimler.indexOf(isim) !== i) { throw new Error(`"${isim}" iki kez seçilmiş`) } } return `Takım hazır: ${sayi} şampiyon`} const denemeler = [ ["Ahri", "Garen", "Jinx"], [], ["Ahri", "Lux", "Garen", "Jinx", "Teemo", "Annie"], ["Ahri", " ", "Lux"], ["Ahri", "Lux", "Ahri"], ["Ahri", "Lux", "Garen", "Jinx", "Teemo"],] for (const isimler of denemeler) { try { console.log(takimKur(isimler)) } catch (hata) { console.log(hata.name + ": " + hata.message) }}Takım hazır: 3 şampiyonError: Takım boş olamazRangeError: En fazla 5 şampiyon, gelen: 6Error: 2. sıradaki ad boşError: "Ahri" iki kez seçilmişTakım hazır: 5 şampiyonSınır denemeleri: tam 5 adlık son dizi geçti, 6 adlık dizi durduruldu; boş dizi döngüye hiç girmeden yakalandı. Bekçi bir sorun bulduğu anda throw fonksiyonu bitirir, bu yüzden bir dizide birden çok sorun varsa yalnızca ilki söylenir.
Ayar dosyası
Ders 12'nin son görevindeki ayarları hatırla: oyunun ayarları {"ses": 70, "dil": "tr"} gibi bir JSON metninde duruyor, varsayılanlar da const varsayilanAyarlar = { ses: 50, dil: "tr" }. İki fonksiyon yaz:
sesDenetle(ses): ses 0 ile 100 arasında değilseRangeErrorfırlatsın:Ses 0-100 olmalı, gelen: 101.ayarOku(metin): metniJSON.parseile çözsün, sesisesDenetleile denetlesin ve ayarları döndürsün. Metin bozuksa (SyntaxError) programı durdurmasın:Ayar bozuk, varsayılanlar geçerliyazıp varsayılan ayarları döndürsün. Bunun dışındaki her hatayı,RangeErrordahil, yeniden fırlatsın; onuayarOku'yu çağıran kod yakalasın.
Deneme kodu:
const metinler = [ '{"ses": 100, "dil": "tr"}', '{"ses": 0, "dil": "en"}', '{"ses": 70, "dil":', '{"ses": 101, "dil": "tr"}',] for (const metin of metinler) { try { console.log(ayarOku(metin)) } catch (hata) { console.log(hata.name + ": " + hata.message) }}Beklenen çıktı:
{ ses: 100, dil: 'tr' }{ ses: 0, dil: 'en' }Ayar bozuk, varsayılanlar geçerli{ ses: 50, dil: 'tr' }RangeError: Ses 0-100 olmalı, gelen: 101İpucu 1
JSON.parse'ı ve sesDenetle çağrısını aynı try'ın içine koy. catch'te hata.name'e bak.
İpucu 2
catch iki yola ayrılıyor: hata.name === "SyntaxError" ise mesajı yaz ve varsayılanları return et; değilse throw hata.
İpucu 3
Ses denetimi: ses < 0 || ses > 100. 0 ve 100 geçerli olmalı; deneme kodunun ilk iki metni tam bu sınırları deniyor.
Çözümü göster
const varsayilanAyarlar = { ses: 50, dil: "tr" } function sesDenetle(ses) { if (ses < 0 || ses > 100) { const mesaj = `Ses 0-100 olmalı, gelen: ${ses}` throw new RangeError(mesaj) }} function ayarOku(metin) { try { const ayar = JSON.parse(metin) sesDenetle(ayar.ses) return ayar } catch (hata) { if (hata.name === "SyntaxError") { console.log("Ayar bozuk, varsayılanlar geçerli") return { ...varsayilanAyarlar } } throw hata }} const metinler = [ '{"ses": 100, "dil": "tr"}', '{"ses": 0, "dil": "en"}', '{"ses": 70, "dil":', '{"ses": 101, "dil": "tr"}',] for (const metin of metinler) { try { console.log(ayarOku(metin)) } catch (hata) { console.log(hata.name + ": " + hata.message) }}{ ses: 100, dil: 'tr' }{ ses: 0, dil: 'en' }Ayar bozuk, varsayılanlar geçerli{ ses: 50, dil: 'tr' }RangeError: Ses 0-100 olmalı, gelen: 101RangeErrorönceayarOku'nun kendicatch'ine düştü;SyntaxErrorolmadığı için orada yeniden fırlatıldı ve deneme kodununcatch'ine ulaştı.throw hatasatırını unutsaydın 101'lik ses sessizce yutulur, fonksiyonundefineddöndürürdü.- Varsayılanları
{ ...varsayilanAyarlar }ile kopyalayıp döndürdük (yayma, Ders 12). Objenin kendisini döndürseydik, onu alıp değiştiren bir kod asıl varsayılan ayarları da bozardı. - Bir açık daha var:
'{"dil": "tr"}'gibi ses alanı hiç olmayan bir metin denetimden geçer ve{ dil: 'tr' }döner, çünküundefined < 0deundefined > 100defalse. İstersen bunun için de bir bekçi ekle.
Kendini sına
Soru 1
Bu program ne yazar?
try { console.log("A") JSON.parse("{") console.log("B")} catch (hata) { console.log("C")} finally { console.log("D")}console.log("E")Cevabı göster
Alt alta A, C, D, E. JSON.parse("{") hata fırlatınca try'ın kalanı (B) atlanır, catch çalışır, ardından finally, en son da try'dan sonraki satır.
Soru 2
Bu program ne yazar?
function seviyeVer() { try { return "Seviye 5" } finally { console.log("finally") }} console.log(seviyeVer())Cevabı göster
Önce finally, sonra Seviye 5. return fonksiyonu bitirmeden önce finally çalışır; dönen değer ancak ondan sonra dışarıdaki console.log'a ulaşır.
Soru 3
Bu program neden undefined yazıyor?
try { throw "Yetersiz mana"} catch (hata) { console.log(hata.message)}Cevabı göster
throw ile bir hata objesi değil, düz bir metin fırlatıldı. Metnin message alanı yoktur, bu yüzden undefined çıkar. throw new Error("Yetersiz mana") yazılsaydı Yetersiz mana yazardı.
Soru 4
Bu program ne yazar? Fonksiyonda catch olmadığına dikkat et.
function yukle() { try { console.log("1") JSON.parse("{") console.log("2") } finally { console.log("3") } console.log("4")} try { yukle()} catch (hata) { console.log("5")}console.log("6")Cevabı göster
Alt alta 1, 3, 5, 6. JSON.parse("{") hata fırlatınca 2 atlanır. yukle'de catch yok: finally yine çalışır (3), ama hata yakalanmadığı için fonksiyon orada biter ve 4 hiç yazılmaz. Hata, yukle'yi çağıran koddaki catch'e düşer (5), program da oradan yoluna devam eder (6).
Soru 5
Yığın izini oku: hata hangi fonksiyonda çıktı, onu kim çağırdı, sebebi ne?
const sampiyonlar = { Ahri: { saldiri: 50 }, Lux: { saldiri: 45 },} function hasarHesapla(ad) { return sampiyonlar[ad].saldiri * 2} const takim = ["Ahri", "Lux", "Teemo"]const hasarlar = takim.map((ad) => hasarHesapla(ad))console.log(hasarlar)C:\kod\ogren\ders13.js:7 return sampiyonlar[ad].saldiri * 2 ^ TypeError: Cannot read properties of undefined (reading 'saldiri') at hasarHesapla (C:\kod\ogren\ders13.js:7:28) at C:\kod\ogren\ders13.js:11:36 at Array.map (<anonymous>) at Object.<anonymous> (C:\kod\ogren\ders13.js:11:24) ... Node.js v24.20.0Cevabı göster
Hata hasarHesapla'da, 7. satırda çıktı: sampiyonlar[ad] undefined, içinden saldiri okunamadı. İkinci satırda fonksiyon adı yok, yalnızca yer var (ders13.js:11:36): bu, 11. satırdaki adsız ok fonksiyonu (ad) => hasarHesapla(ad). Onu Array.map çağırdı; <anonymous> yazması, map'in senin dosyanda değil JavaScript'in içinde olmasından. map'i de ana program 11. satırda çağırdı. Sebep: takim dizisindeki "Teemo", sampiyonlar objesinde yok.
Soru 6
Hangisinde try / catch kullanmak doğru, hangisinde değil?
- Kullanıcının yazdığı bir JSON metnini çözmek.
- Bir değişkenin adını yanlış yazmış olma ihtimaline karşı bütün programı tek bir
try'ın içine almak. - Boş olabilecek bir dizinin ilk elemanının
isimalanını okumak. - Dosyandaki bir yazım hatası (
SyntaxError) programı durdurmasın diye o satırıtry'ın içine almak.
Cevabı göster
- Doğru. Dışarıdan gelen metin bozuk olabilir ve bunu önceden denetleyemezsin; yakala ve kullanıcıya söyle.
- Yanlış. Yanlış yazılmış bir ad
ReferenceErrorverir;catchonu yakalayabilir ama yakalamamalı, çünkü bu düzeltilmesi gereken bir kod hatası. Bütün programı tek birtry'a sarmak bu hataları gizler; program dursun, yığın izi sana yeri göstersin. - Yanlış. Bunu önceden denetleyebilirsin:
if (dizi.length === 0)ya dadizi[0]?.isim.try,ifile önleyemediğin durumlar içindir. - İşe yaramaz. Node dosyayı çalıştırmadan önce okur; yazım hatası bulursa tek satır bile çalıştırmaz,
trydevreye bile girmez. Yazım hatası yakalanmaz, düzeltilir.
Soru 7
RangeError: Maximum call stack size exceeded görünce ilk neye bakarsın?
Cevabı göster
Yığın izine. Aynı fonksiyon adı alt alta tekrar ediyorsa o fonksiyon kendini (ya da iki fonksiyon birbirini) durmadan çağırıyordur. Tekrar eden satıra git (dosya.js:satır) ve çağrının neden hiç bitmediğine bak; çoğu zaman yanlış fonksiyonu çağırmışsındır.
Kayıt noktası
Bu dersin çalışmasını Git'e kaydet. VS Code terminalinde, C:\kod\ogren klasöründeyken:
Bir sonraki derste kodunu dosyalara bölmeyi (import / export) ve npm ile başkalarının yazdığı paketleri kullanmayı öğreneceksin.