Blok 2 · Ders 9
Metinler ve sayılarla çalışmak
Şablon metinler, metin metotları, sayı ile metin arasında dönüşüm ve Math ile oyun istatistiklerini düzgün biçimde gösteriyorsun.
Bu dersin sonunda
- Şablon metin ve
${ }yuvalarıyla değişkenleri ve hesapları metnin içine yerleştirebilirsin - Metin metotlarıyla adları kısaltabilir, sohbet mesajlarını denetleyebilir, metni parçalara bölebilir ve hizalı tablolar yazdırabilirsin
- Türkçe harfleri
toLocaleUpperCasevetoLocaleLowerCaseile doğru büyütüp küçültebilirsin - Metni sayıya, sayıyı metne çevirebilir;
NaNdeğeriniNumber.isNaNile yakalayabilirsin Mathile yuvarlayıp sınırlayabilir,0.1 + 0.2tuzağına düşmeden karşılaştırabilirsin- Bir maç geçmişinden KDA ve kazanma oranını, sıfıra bölmeye karşı korunmuş olarak hesaplayıp gösterebilirsin
Bu sayfada
Şimdiye kadar oyun verileriyle hesap yaptın ama sonuçları ekrana pek düzgün yazdıramadın: + ile uzayan satırlar, 3.3333333333333335 gibi bitmeyen sayılar, büyük-küçük harf yüzünden tutmayan karşılaştırmalar. Bu derste metinleri ve sayıları istediğin biçime sokmayı öğreniyorsun. Sonunda bir maç geçmişini okuyup her maçın KDA'sını ve kazanma oranını hizalı bir tabloyla gösteren bir program yazacaksın.
Şablon metin: artı zincirine son
Ahri'nin maç skorunu Ders 1'den bildiğin yolla, + ile yazdır:
const isim = "Ahri"const kill = 7const olum = 2const asist = 9console.log(isim + " " + kill + "/" + olum + "/" + asist)Ahri 7/2/9Çalışıyor ama okuması zor: tırnak aç, kapat, artı, yine tırnak… Bir boşluğu unutmak ya da bir tırnağı yanlış yere koymak işten bile değil. Aynı satırı şablon metin ile yaz:
const isim = "Ahri"const kill = 7const olum = 2const asist = 9console.log(`${isim} ${kill}/${olum}/${asist}`)Ahri 7/2/9Metin çift tırnakla değil, ters tırnakla açılıp kapanıyor ve değişkenler metnin içine ${ } ile yerleşiyor. JavaScript ${isim} gördüğü yerde isim kutusunun içindekini yazar. Çıktı aynı, ama kod artık çıktının neye benzeyeceğini gösteriyor: boşluklar ve eğik çizgiler tam yerinde.
${ } yuvalarına değerler yerleşir, gerisi olduğu gibi kalır.Ters tırnak nasıl yazılır?
Ders 0'daki klavye tablosunda gördün: Türkçe Q klavyede ters tırnak AltGr + , ile yazılır. Virgül tuşu orta sıranın en sağında, İ'nin yanında ve Enter'ın hemen solundadır. AltGr, boşluk çubuğunun sağındaki Alt tuşudur.
Bu tuş bir ölü tuştur (dead key): basınca ekranda hemen bir şey çıkmaz, Windows bir sonraki tuşu bekler. Ardından Boşluk'a basınca ` belirir. Bu işareti Ders 7'de README'deki kod bloklarını yazarken de kullanmıştın.
Yuvaya hesap da yazılır
${ } yuvasının içine sonucu bir değer olan her şeyi yazabilirsin: değişken, obje alanı (Ders 4), dizi elemanı (Ders 5), hesap ya da değer döndüren bir fonksiyon çağrısı (Ders 6). JavaScript önce yuvanın içini hesaplar, sonucu metne çevirip oraya koyar:
const ahri = { isim: "Ahri", hp: 640, maxHp: 800 }console.log(`${ahri.isim}: ${ahri.hp}/${ahri.maxHp} can`)console.log(`Can yüzdesi: ${ahri.hp / ahri.maxHp * 100}`)console.log(`Yarısı: ${ahri.maxHp / 2}`)Ahri: 640/800 canCan yüzdesi: 80Yarısı: 400Şablon metnin içinde çift tırnak da serbesttir: `"${isim}" bulunamadı` yazabilirsin. Çift tırnaklı bir metnin içine çift tırnak koymaya kalksaydın metin erken kapanırdı.
Çok satırlı metin
Çift tırnaklı metin aynı satırda bitmek zorundadır. Ters tırnaklı metin ise birden çok satıra yayılabilir; satır sonları da metnin parçası olur:
const isim = "Lux"const seviye = 11const kart = `Şampiyon: ${isim}Seviye: ${seviye}Durum: hazır`console.log(kart)PS C:\kod\ogren> node ders9.jsŞampiyon: LuxSeviye: 11Durum: hazırŞablon metin 3. satırda açılıp 5. satırda kapanıyor. Arada bastığın her Enter çıktıda yeni bir satır oldu.
Yuva yalnızca ters tırnakta çalışır
console.log("KDA: ${kda}")console.log(`KDA: ${kda}`)Çift ya da tek tırnak içindeki ${kda} sıradan bir yazıdır: ekrana harfi harfine basılır ve hata mesajı çıkmadığı için gözden kaçar. Ters tırnağı ona benzeyen kesme işaretiyle (') karıştırırsan da yuva çalışmaz.
Metin metotları: metne iş yaptırmak
Ders 5'te diziye takim.push("Lux") ile eleman ekledin; değerin arkasına nokta koyup bir metot çağırdın. Metinlerin de hazır metotları var: büyük harfe çevirmek, içinde aramak, parçalara bölmek, boşluk temizlemek… Hepsi aynı kalıpla yazılır: metin.metotAdi(…); parantezin içine metodun argümanları gelir.
Önemli bir fark var: push dizinin kendisini değiştiriyordu, metin metotları ise metne dokunmaz. Büyütülmüş, kırpılmış ya da değiştirilmiş hâlini yeni bir metin olarak döndürürler. Sonucu kullanmak istiyorsan ya yazdırırsın ya da bir kutuya koyarsın:
let mesaj = "gg wp"mesaj.toUpperCase()console.log(mesaj)mesaj = mesaj.toUpperCase()console.log(mesaj)gg wpGG WPİkinci satır büyük harfli yeni metni üretti ama hiçbir yere koymadı; Ders 1'deki "hesaplamak yetmez, koymak gerekir" tuzağının aynısı. Dördüncü satır sonucu mesaj kutusuna geri koyduğu için değişiklik kaldı.
length: kaç karakter?
const yer = "Sihirdar Vadisi"console.log(yer.length)console.log(yer[0])console.log(yer[yer.length - 1])15Silength boşluk dahil karakter sayısını verir. Metin, harflerden oluşan bir dizi gibidir: her karakterin 0'dan başlayan bir sıra numarası vardır ve son karakter length - 1 numaradadır. Dizilerdeki gibi length bir metot değil, bir alandır; sonuna parantez koyarsan TypeError: yer.length is not a function alırsın.
toUpperCase ve toLowerCase: büyük-küçük harf
Ders 2'den biliyorsun: "TEEMO" ile "Teemo" JavaScript için iki ayrı metindir. Oyuncunun nasıl yazdığını bilemezsin; karşılaştırmadan önce iki tarafı da aynı biçime getir:
const yazilan = "TEEMO"console.log(yazilan.toLowerCase())console.log(yazilan === "Teemo")console.log(yazilan.toLowerCase() === "teemo")teemofalsetruetoUpperCase() bütün harfleri büyütür, toLowerCase() küçültür. İkisi de argüman almaz ama parantezleri şarttır. Türkçe harflerde bu iki metodun bir tuzağı var; ona ayrı bir bölüm ayırdık.
includes ve startsWith: sohbet filtresi
const mesaj = "Bu Teemo tam bir NOOB"console.log(mesaj.includes("Teemo"))console.log(mesaj.includes("noob"))console.log(mesaj.toLowerCase().includes("noob"))truefalsetrueincludes, aradığın parça metnin herhangi bir yerinde geçiyorsa true, geçmiyorsa false döndürür; Ders 5'te dizilerde kullandığın includes'un metin hâli. Büyük-küçük harfe duyarlıdır, bu yüzden ikinci satır false.
Üçüncü satırda iki metot art arda çalıştı: önce toLowerCase() küçük harfli yeni bir metin üretti, includes o yeni metnin üstünde çalıştı. Buna zincirleme denir. Dizilerle Ders 11'de çok daha uzun zincirler kuracaksın.
startsWith ise yalnızca metnin başına bakar. Sohbette / ile başlayan mesajlar komuttur:
const mesaj = "/mute Teemo"if (mesaj.startsWith("/mute ")) { console.log("Susturma komutu")} else { console.log("Normal mesaj")}Susturma komutuslice: bir parçayı kesip almak
slice(baslangic, bitis) metnin bir parçasını kopyalar: başlangıçtaki karakter dahil, bitişteki hariç. Akılda tutmanın iyi bir yolu, numaraları harflerin üstüne değil aralarına çizilmiş kesim çizgileri gibi düşünmek. slice(1, 3) "1 numaralı çizgiden kes, 3 numaralı çizgiye kadar al" demektir:
"Garen".slice(1, 3): 1 ve 3 numaralı kesim çizgileri arasındaki harfler alınır. Başlangıç dahil, bitiş hariç.const isim = "Garen"console.log(isim.slice(0, 3))console.log(isim.slice(1, 3))console.log(isim.slice(2))console.log(isim.slice(-2))console.log(isim.slice(0, 99))GararrenenGarenslice(0, 3): 0 ile 3 numaralı çizgiler arası, yani ilk üç harf.- Tek argüman verirsen sona kadar gider:
slice(2). - Eksi sayı sondan sayar:
slice(-2)son iki harf. - Sınırın ötesine taşarsan hata çıkmaz, elinde ne varsa o gelir:
slice(0, 99).
startsWith ile birlikte kullanınca komutun hedefini ayıklayabilirsin. "/mute " altı karakterdir (eğik çizgi, dört harf, boşluk), bu yüzden hedef 6 numaralı çizgiden başlar:
const mesaj = "/mute Teemo"if (mesaj.startsWith("/mute ")) { const hedef = mesaj.slice(6) console.log(`${hedef} susturuldu`)}Teemo susturuldusplit: metni diziye bölmek
const takimMetni = "Ahri,Lux,Garen"const takim = takimMetni.split(",")console.log(takim)console.log(takim.length)for (const sampiyon of takim) { console.log(`- ${sampiyon}`)}[ 'Ahri', 'Lux', 'Garen' ]3- Ahri- Lux- Garensplit(ayirici) metni ayırıcının geçtiği her yerden keser ve parçaları bir dizi olarak döndürür; ayırıcının kendisi atılır. Sonuç Ders 5'teki dizilerden farksızdır: length'i var, for...of ile gezebilirsin. Dışarıdan gelen virgüllü listeler, 7/2/9 gibi skorlar, Ad#Etiket biçimindeki oyuncu kimlikleri hep böyle parçalanır.
trim: baştaki ve sondaki boşluklar
Kullanıcının yazdığı metinler (ileride klavyeden ve formlardan alacaksın) çoğu zaman başında ya da sonunda fazladan boşlukla gelir. trim() bu boşlukları atar, aradakilere dokunmaz:
const girilen = " Sihirdar Vadisi "console.log(`[${girilen}]`)console.log(`[${girilen.trim()}]`)console.log(girilen.trim() === "Sihirdar Vadisi")[ Sihirdar Vadisi ][Sihirdar Vadisi]trueKöşeli parantezleri yalnızca boşlukları görebilmek için koyduk; terminalde boşluk görünmez.
replace ve replaceAll: değiştirmek
const mesaj = "noob Teemo, gerçekten noob"console.log(mesaj.replace("noob", "****"))console.log(mesaj.replaceAll("noob", "****"))**** Teemo, gerçekten noob**** Teemo, gerçekten ****replace(aranan, yeni) yalnızca ilk bulduğunu değiştirir; hepsini değiştirmek için replaceAll var. İkisi de büyük-küçük harfe duyarlıdır, yani "NOOB" yakalanmaz. Aranan parça hiç yoksa metin olduğu gibi geri gelir, hata çıkmaz.
padStart ve padEnd: hizalı tablo
Ders 8'deki skor tablonun sütunları hizasız kalmıştı: adlar farklı uzunlukta olunca arkalarındaki sayılar kayıyordu. Sütunları hizalamak için her hücreyi aynı uzunluğa getirmen gerekir. padEnd(uzunluk) metnin sonuna, padStart(uzunluk) başına boşluk ekleyerek onu o uzunluğa tamamlar. Dükkândaki eşya listesiyle dene:
console.log("Kılıç".padEnd(8) + "450".padStart(6))console.log("İksir".padEnd(8) + "50".padStart(6))console.log("Kalkan".padEnd(8) + "1100".padStart(6))Kılıç 450İksir 50Kalkan 1100Adlar sola, fiyatlar sağa yaslandı; sağa yaslanan sayıların basamakları alt alta gelir. İkinci argüman boşluk yerine eklenecek karakteri söyler: "7".padStart(2, "0") sonucu "07". Metin zaten o uzunluktaysa ya da daha uzunsa hiçbir şey eklenmez, metin kısaltılmaz da; sütun genişliğini en uzun değere göre seç.
padStart bir metin metodudur. Sayıda çağırırsan program durur; çözümünü Sık hatalar bölümünde göreceksin.
Bu bölümdeki metotların hepsi bir arada:
| Ne yapar? | Örnek | Sonuç |
|---|---|---|
| Uzunluk (alan) | "Lux".length | 3 |
| Büyük harf | "gg".toUpperCase() | "GG" |
| Küçük harf | "GG".toLowerCase() | "gg" |
| İçinde var mı? | "gg wp".includes("wp") | true |
| Bununla mı başlıyor? | "/all".startsWith("/") | true |
| Parça kopyala | "Garen".slice(0, 3) | "Gar" |
| Böl, dizi ver | "7/2/9".split("/") | [ '7', '2', '9' ] |
| Kenar boşluklarını at | " Lux ".trim() | "Lux" |
| İlkini değiştir | "a-b-c".replace("-", "+") | "a+b-c" |
| Hepsini değiştir | "a-b-c".replaceAll("-", "+") | "a+b+c" |
| Başa ekleyip tamamla | "7".padStart(3, "0") | "007" |
| Sona ekleyip tamamla | "Lux".padEnd(5, ".") | "Lux.." |
Türkçe harf tuzağı: i ile İ
toUpperCase ve toLowerCase hangi dilde yazdığını bilmez; İngilizceye uyan, dilden bağımsız kurallarla çalışır. İngilizcede noktalı ve noktasız i ayrımı yoktur: küçük i'nin büyüğü I'dır. Türkçede ise iki ayrı çift var: ı ile I, i ile İ. Sonuç:
console.log("iyi oyunlar".toUpperCase())console.log("İYİ OYUNLAR".toLowerCase())console.log("İYİ OYUNLAR".toLowerCase().includes("iyi"))console.log("İ".toLowerCase().length)IYI OYUNLARi̇yi̇ oyunlarfalse2İlk satırda noktalar kayboldu. İkinci satır daha sinsi: İ küçültülünce ekranda i'ye benzeyen ama iki karakterden oluşan bir şey çıktı: bir i ve onun üstüne binen ayrı bir nokta işareti. Fark etmeyebilirsin, ama dördüncü satır uzunluğun 2 olduğunu söylüyor. Üçüncü satırın false vermesinin sebebi bu: sohbet filtren "İYİ" yazan herkesi kaçırır.
Çözüm, dili söyleyerek çevirmek:
console.log("iyi oyunlar".toLocaleUpperCase("tr"))console.log("İYİ OYUNLAR".toLocaleLowerCase("tr"))console.log("İYİ OYUNLAR".toLocaleLowerCase("tr").includes("iyi"))İYİ OYUNLARiyi oyunlartruetoLocaleUpperCase ve toLocaleLowerCase parantezde verdiğin dilin kurallarını kullanır; "tr" Türkçe demek.
Türkçe kurallar da her yerde doğru değildir. "tr" her I'yı ı yapar; İngilizce bir sözcüğü Türkçe kuralla küçültürsen bu kez o bozulur:
console.log("DOUBLE KILL".toLocaleLowerCase("tr"))console.log("DOUBLE KILL".toLowerCase())double kılldouble killKural: metin hangi dildeyse onun kuralını kullan. Türkçe metinde toLocaleUpperCase("tr") ve toLocaleLowerCase("tr"); İngilizce sözcüklerde, komutlarda ve oyuncu etiketi gibi kodlarda düz toUpperCase ve toLowerCase.
Metinden sayıya, sayıdan metne
Ders 1'de "5" + 3 işleminin 53 verdiğini görmüştün: +'nın bir yanında metin varsa toplamaz, yapıştırır. Sorun şu ki dışarıdan gelen sayılar çoğu zaman metin olarak gelir: klavyeden yazılan, dosyadan okunan, bir siteden gelen ya da split ile ayrılan her şey. Hesaba başlamadan önce onları sayıya çevirmen gerekir.
const altinMetni = "300"console.log(altinMetni + 50)console.log(Number(altinMetni) + 50)30050350Number, parseInt, parseFloat
Metni sayıya çevirmenin üç yolu var. Farkları, metin tam bir sayı olmadığında ortaya çıkar:
console.log(Number("12 kill"), parseInt("12 kill"))console.log(Number("3.5"), parseInt("3.5"), parseFloat("3.5"))console.log(Number("3,5"), parseFloat("3,5"))console.log(Number(""), parseInt(""))NaN 123.5 3 3.5NaN 30 NaN| Metin | Number | parseInt | parseFloat |
|---|---|---|---|
"42" | 42 | 42 | 42 |
"3.5" | 3.5 | 3 | 3.5 |
"12 kill" | NaN | 12 | 12 |
"kill 12" | NaN | NaN | NaN |
"3,5" | NaN | 3 | 3 |
" 7 " | 7 | 7 | 7 |
"" (boş) | 0 | NaN | NaN |
Numbermetnin tamamının sayı olmasını ister; değilseNaNverir (birazdan ayrıntısıyla). Baştaki ve sondaki boşlukları affeder. Boş metni ise0sayar: kullanıcı hiçbir şey yazmasa da elinde bir0olur, dikkat.parseIntbaştan okumaya başlar, sayı olmayan ilk karakterde durur ve o ana kadar okuduğu tam sayıyı verir. Küsuratı yuvarlamaz, keser:parseInt("3.9")sonucu3.parseFloatda baştan okur ama ondalıklı sayıyı da alır.
Hangisini seçmeli? Kural olarak Number: metin tam bir sayı değilse bunu sana açıkça söyler. parseInt ve parseFloat'ı yalnızca "12 kill", "3.5 saniye" gibi sayının arkasından yazı geldiğini bildiğin durumlarda kullan.
Sayıdan metne: String, şablon metin ve toFixed
Ters yönde de birkaç yol var. String(sayi) sayıyı metne çevirir; şablon metne koymak da aynı işi görür:
const kill = 7console.log(String(kill) + 1)console.log(`${kill}` + 1)console.log(kill + 1)71718Ondalıklı bir sayıyı belli sayıda basamakla göstermek için toFixed(basamak) var. Fazla basamakları yuvarlayarak atar, eksik basamakları sıfırla doldurur:
const kda = 16 / 3console.log(kda)console.log(kda.toFixed(2))console.log(kda.toFixed(0))console.log((5).toFixed(2))5.3333333333333335.3355.00Son satırdaki parantez şart. 5.toFixed(2) yazarsan JavaScript 5.'i ondalıklı bir sayının başı sanır ve SyntaxError: Invalid or unexpected token verir. Değişkenle (kda.toFixed(2)) böyle bir sorun yok.
NaN olur.NaN: sayı olmayan sayı
Number("12 kill") sonucundaki NaN, İngilizce Not a Number (sayı değil) sözünün kısaltması. JavaScript bir sayı üretmesi gerekirken üretemediğinde program durmaz; bu özel değeri verir ve yoluna devam eder.
Ders 3'te "R: " + maliyet - mana satırının NaN verdiğini görmüş, sebebini bu derse bırakmıştık. + bir metinle karşılaşınca yapıştırır. -, * ve / ise yalnızca sayılarla çalışır, bu yüzden JavaScript metni önce sessizce sayıya çevirmeyi dener:
console.log("300" - 50)console.log("300" + 50)console.log("R: 100" - 90)25030050NaN"300" sayıya çevrilebildiği için çıkarma çalıştı; "R: 100" çevrilemedi ve NaN oldu. Bu sessiz çevirmeye güvenme: hesaba girecek metni her zaman kendin, Number ile çevir.
NaN'ın asıl tehlikesi bulaşıcı olması. Ahri'nin objesinde olum alanını olumm diye yanlış yazdığını düşün:
const ahri = { kill: 7, olum: 2, asist: 9 }const kda = (ahri.kill + ahri.asist) / ahri.olummconsole.log(kda)console.log(kda * 2 + 1)console.log(`KDA: ${kda.toFixed(2)}`)NaNNaNKDA: NaNolumm diye bir alan yok, değeri undefined (Ders 4). undefined ile bölme NaN verdi, NaN da girdiği her hesaba bulaştı. Program hiç hata vermedi ve yanlış sonuç sessizce ekrana kadar geldi: bir hata mesajı yeri gösterirdi, NaN'ın izini ise sen sürmelisin. Bu bir mantık hatası: kod çalışıyor, sonuç yanlış.
NaN nasıl yakalanır?
Akla ilk gelen deger === NaN hiçbir zaman çalışmaz: NaN hiçbir şeye eşit değildir, kendisine bile.
const sayi = Number("on iki")console.log(sayi === NaN)console.log(Number.isNaN(sayi))falsetrueDoğru araç Number.isNaN(deger): değer NaN ise true döndürür. Dışarıdan gelen bir metni sayıya çevirdikten hemen sonra bununla denetle:
const girilen = "12 kill"const kill = Number(girilen)if (Number.isNaN(kill)) { console.log(`"${girilen}" bir sayı değil`)} else { console.log(`Kill: ${kill}`)}"12 kill" bir sayı değilgirilen'i "12" yapıp yeniden çalıştır: bu kez Kill: 12 yazmalı. İki kolu da denemek, Ders 2'den beri yaptığın sınır değeri denemesinin bir parçası.
Math ile yuvarlamak ve sınırlamak
Ders 1'de 10 / 3 sonucunun 3.3333333333333335 çıktığını görmüş, yuvarlamayı bu derse bırakmıştık. Ders 6'da da Math.floor ile rastgele hasarın küsuratını atmıştın. Math, hesap araçlarının durduğu hazır bir obje; içinde yuvarlamak ve sınırlamak için başka araçlar da var.
round, floor, ceil
console.log(Math.round(2.4), Math.round(2.5), Math.round(2.6))console.log(Math.floor(2.4), Math.floor(2.9))console.log(Math.ceil(2.1), Math.ceil(2.9))2 3 32 23 3Math.round: en yakın tam sayıya gider; tam ortadaki.5yukarı yuvarlanır.Math.floor: her zaman aşağı (İngilizcesi floor, taban).Math.ceil: her zaman yukarı (İngilizcesi ceiling, tavan).
floor hep aşağıdaki, ceil hep yukarıdaki tam sayıya gider; round hangisi yakınsa ona.Hangisini ne zaman seçeceğini, soruyu oyuna çevirerek bulursun:
const deneyim = 1000const seviyeBasina = 280console.log(`Tam seviye: ${Math.floor(deneyim / seviyeBasina)}`) const baronCani = 3600const saniyeHasari = 250console.log(`Baron ${Math.ceil(baronCani / saniyeHasari)} saniyede düşer`) const canYuzdesi = 640 / 900 * 100console.log(`Can: %${Math.round(canYuzdesi)}`)Tam seviye: 3Baron 15 saniyede düşerCan: %711000 / 280yaklaşık3.57. Yarım seviye sayılmaz; kaç tam seviye atlandığınıfloorverir.3600 / 250sonucu14.4. 14 saniyede düşmeyen Baron'a 15. saniyede de vurman gerekir: "kaç tane gerekir?" sorusunun cevabıceil.- Ekranda gösterilecek bir yüzde için en yakını,
round.
max, min ve abs
console.log(Math.max(3, 8, 5))console.log(Math.min(3, 8, 5))console.log(Math.abs(-1200))831200Math.max verdiğin sayıların en büyüğünü, Math.min en küçüğünü döndürür. Oyunda asıl işleri sınırlamaktır: can 0'ın altına inmemeli, iksir canı en yüksek değerin üstüne çıkarmamalı.
const maxHp = 800let hp = 120hp = Math.max(0, hp - 300)console.log(`Darbeden sonra: ${hp}`)hp = Math.min(maxHp, hp + 1000)console.log(`İksirden sonra: ${hp}`)Darbeden sonra: 0İksirden sonra: 800Math.max(0, hp - 300) şunu der: "hp - 300 ile 0'dan hangisi büyükse onu al." Sonuç eksiye düşerse 0 kazanır. Ders 2'de bunu bir if ile yapardın; Math.max aynı işi tek satırda görür. Math.min(maxHp, …) de tavanı koyar.
Math.abs bir sayının eksi işaretini atar, yalnızca büyüklüğünü bırakır. İki takım arasındaki altın farkını, kimin önde olduğuna bakmadan hesaplamak için:
const maviAltin = 38750const kirmiziAltin = 41200console.log(maviAltin - kirmiziAltin)console.log(`Altın farkı: ${Math.abs(maviAltin - kirmiziAltin)}`)-2450Altın farkı: 2450Belli bir basamağa yuvarlamak
Math.round hep tam sayıya yuvarlar. Bir basamak istiyorsan sayıyı 10 ile çarp, yuvarla, 10'a böl; iki basamak için 100 ile:
const kda = 16 / 3console.log(Math.round(kda * 10) / 10)console.log(Math.round(kda * 100) / 100)console.log(kda.toFixed(2))5.35.335.33Son iki satır aynı görünüyor ama biri sayı, öteki metin:
| Ne yapacaksın? | Kullan |
|---|---|
| Hesaba devam edeceksin | Math.round(x * 100) / 100 sayı verir: 5.33 |
| Yalnızca ekrana yazacaksın | x.toFixed(2) metin verir: "5.33", hep iki basamaklı ("5.00") |
Ondalık tuzağı: 0.1 + 0.2
Şunu çalıştırmadan önce sonucu tahmin et:
console.log(0.1 + 0.2)console.log(0.1 + 0.2 === 0.3)0.30000000000000004falseJavaScript bozuk değil. Bilgisayar sayıları ikilik sistemde, yalnızca 0 ve 1 ile saklar. Onluk sistemde üçte biri sonlu sayıda basamakla yazamadığın gibi (0.3333…), ikilik sistemde de 0.1 sonlu basamakla yazılamaz.
Bilgisayar bir yerde keser ve küçük bir yuvarlama payı kalır; Ders 1'deki 3.3333333333333335'in sonundaki 5 de bu kesmenin iziydi. Bu JavaScript'e özgü değil: Python, Java, C# gibi dillerde de 0.1 + 0.2, 0.3'e eşit çıkmaz.
Bu küçük pay, ekranda çirkin durmasından çok karşılaştırmaları bozduğu için önemlidir. Çözümler:
const toplam = 0.1 + 0.2console.log(toplam.toFixed(2))console.log(Math.round(toplam * 100) / 100)console.log(Math.round(toplam * 100) / 100 === 0.3)0.300.3true- Ekranda gösterirken
toFixedile basamak sayısını sınırla. - Ondalıklı iki sayıyı karşılaştırmadan önce ikisini de aynı basamağa yuvarla.
- Kuruşu kuruşuna doğru olması gereken değerleri, örneğin parayı, tam sayı olarak tut: 19,99 lirayı
19.99değil1999kuruş olarak saklamak bu sorunu baştan ortadan kaldırır. Oyundaki altın zaten tam sayıdır; tuzak yalnızca ondalıklı değerlerde çıkar.
Uygulama: KDA ve kazanma oranı
Şimdi hepsini birleştir. LoL'ün maç geçmişinde skor 7/2/9 biçiminde yazılır: kill, ölüm, asist. KDA, kill ve asist toplamının ölüm sayısına bölümüdür; ne kadar yüksekse oyuncu o kadar az ölüp o kadar çok katkı vermiştir. Programı adım adım kuracağız: hedef, birkaç maçlık geçmişi metin olarak alıp her maçın KDA'sını, genel KDA'yı ve kazanma oranını hizalı bir tabloda göstermek.
Adım 1: skoru parçala
const skor = "7/2/9"const parcalar = skor.split("/")console.log(parcalar)console.log(parcalar[0] + parcalar[2])[ '7', '2', '9' ]79Kill ile asisti toplamak isterken 79 çıktı: dizideki tek tırnaklar parçaların metin olduğunu söylüyor, + de onları yapıştırdı. Her parçayı sayıya çevir:
const parcalar = "7/2/9".split("/")const kill = Number(parcalar[0])const olum = Number(parcalar[1])const asist = Number(parcalar[2])const kda = (kill + asist) / olumconsole.log(`KDA: ${kda.toFixed(2)}`)KDA: 8.00Adım 2: hiç ölmeyen oyuncu
Aynı kodu 10/0/6 skoruyla çalıştır:
const parcalar = "10/0/6".split("/")const kill = Number(parcalar[0])const olum = Number(parcalar[1])const asist = Number(parcalar[2])const kda = (kill + asist) / olumconsole.log(`KDA: ${kda.toFixed(2)}`)KDA: InfinitySıfıra bölmek hata vermez, Infinity (sonsuz) verir; bunu Ders 1'de görmüştün. toFixed de onu olduğu gibi metne çevirdi. Maç istatistiği gösteren siteler bu durumda genellikle "Perfect" (kusursuz) yazar; sen de bölmeden önce ölüm sayısına bakmalısın. İşi bir fonksiyona koy:
function kdaMetni(kill, olum, asist) { if (olum === 0) { return "Kusursuz" } return ((kill + asist) / olum).toFixed(2)} console.log(kdaMetni(7, 2, 9))console.log(kdaMetni(10, 0, 6))console.log(kdaMetni(1, 8, 2))console.log(kdaMetni(0, 0, 0))8.00Kusursuz0.38Kusursuzolum 0 ise fonksiyon erken çıkış yapar ve bölme satırına hiç gelinmez. Son satıra dikkat: hiç oynamamış gibi duran 0/0/0 da "Kusursuz" sayıldı. Hata değil ama tartışmalı bir sonuç; "çalışıyor" ile "doğru" arasındaki farkı yine sınır değeri gösterdi. İstersen kill + asist 0 iken ayrı bir metin döndürebilirsin.
Adım 3: kazanma oranı
function oranMetni(galibiyet, macSayisi) { const yuzde = galibiyet / macSayisi * 100 return `%${yuzde.toFixed(1)}`} console.log(oranMetni(3, 5))console.log(oranMetni(2, 3))console.log(oranMetni(0, 0))%60.0%66.7%NaN0 / 0 sonucu Infinity değil NaN'dır: sıfırın sıfıra bölümü tanımsızdır. Hiç maç oynamamış birine %NaN göstermek istemezsin; yine bölmeden önce denetle. Bir de Türkçede ondalık ayırıcı virgüldür ve yüzde işareti sayının önüne gelir. toFixed'in verdiği metindeki noktayı replace ile virgüle çevir:
function oranMetni(galibiyet, macSayisi) { if (macSayisi === 0) { return "maç yok" } const yuzde = galibiyet / macSayisi * 100 return `%${yuzde.toFixed(1).replace(".", ",")}`} console.log(oranMetni(3, 5))console.log(oranMetni(2, 3))console.log(oranMetni(0, 0))%60,0%66,7maç yokSıra önemli: önce hesap (sayı), sonra toFixed (metin), en son replace (metin). Virgüllü metin artık hesaba girmemeli; Number onu anlamaz.
Hepsi bir arada
Maç geçmişi tek bir metin olarak geliyor: maçlar ", " ile ayrılmış, her maçta skor ve sonuç var (G galibiyet, M mağlubiyet). ders9.js'in içini boşaltıp programın tamamını yaz:
function kdaMetni(kill, olum, asist) { if (olum === 0) { return "Kusursuz" } return ((kill + asist) / olum).toFixed(2)} function oranMetni(galibiyet, macSayisi) { if (macSayisi === 0) { return "maç yok" } const yuzde = galibiyet / macSayisi * 100 return `%${yuzde.toFixed(1).replace(".", ",")}`} const gecmis = "7/2/9 G, 3/5/4 M, 10/0/6 G, 1/8/2 M, 5/3/11 G"const maclar = gecmis.split(", ")let toplamKill = 0let toplamOlum = 0let toplamAsist = 0let galibiyet = 0 console.log("Maç Skor KDA Sonuç")for (let i = 0; i < maclar.length; i++) { const parcalar = maclar[i].split(" ") const skor = parcalar[0] const sonuc = parcalar[1] const sayilar = skor.split("/") const kill = Number(sayilar[0]) const olum = Number(sayilar[1]) const asist = Number(sayilar[2]) toplamKill = toplamKill + kill toplamOlum = toplamOlum + olum toplamAsist = toplamAsist + asist if (sonuc === "G") { galibiyet++ } const sira = String(i + 1).padEnd(5) const kda = kdaMetni(kill, olum, asist).padEnd(10) console.log(`${sira}${skor.padEnd(9)}${kda}${sonuc}`)} const genelKda = kdaMetni(toplamKill, toplamOlum, toplamAsist)const oran = oranMetni(galibiyet, maclar.length)console.log(`Genel KDA: ${genelKda}`)console.log(`Kazanma oranı: ${oran} (${galibiyet}/${maclar.length})`)Maç Skor KDA Sonuç1 7/2/9 8.00 G2 3/5/4 1.40 M3 10/0/6 Kusursuz G4 1/8/2 0.38 M5 5/3/11 5.33 GGenel KDA: 3.22Kazanma oranı: %60,0 (3/5)Programı yukarıdan aşağı oku:
split(", ")geçmişi maçlara ("7/2/9 G"…),split(" ")her maçı skor ve sonuca,split("/")da skoru üç parçaya böler. Her parçaNumberile sayıya çevrilir.- Toplamlar döngüden önce açılan kutularda birikir (Ders 5'teki saymak ve toplamak kalıbı).
- Her satır
padEndile aynı genişliğe tamamlanmış hücrelerden kurulur. Sıra numarası sayı olduğu için önceStringile metne çevrildi;kdaMetniise zaten metin döndürüyor. - Genel KDA, maçların KDA'larının ortalaması değil: bütün kill ve asistlerin toplamı, bütün ölümlerin toplamına bölünür. Böylece ölümsüz geçen "Kusursuz" maçlar da hesaba katılır.
Sınırları sen dene: geçmişe 0/0/0 M gibi bir maç ekle, sonra gecmis'i boş metin ("") yap. Boş geçmişte tabloda NaN ve undefined göreceksin: "".split(", ") boş bir dizi değil, içinde tek bir boş metin olan bir dizi verir. Bozuk girdiyle başa çıkmayı Ders 13'te öğreneceksin; şimdilik bu sonuçların nereden geldiğini açıklayabilmen yeter.
Sık hatalar
Bu dersin hataları. Bütün derslerin hatalarını hata rehberinde de bulabilirsin.
TypeError: kill.padStart is not a functionNe zaman çıkar? Bir metin metodunu sayıda çağırınca. Sayıların padStart, toUpperCase, includes, slice gibi metotları yoktur.
const kill = 7console.log(kill.padStart(3))C:\kod\ogren\ders9.js:2console.log(kill.padStart(3)) ^ TypeError: kill.padStart is not a function at Object.<anonymous> (C:\kod\ogren\ders9.js:2:18) ... Node.js v24.20.0Nasıl düzelir? "is not a function": sayıların padStart diye bir metodu yok. Önce metne çevir: String(kill).padStart(3).
TypeError: oran.toFixed is not a functionNe zaman çıkar? Tersi: sayı metodu olan toFixed'i metinde çağırınca. Değer çoğu zaman split'ten ya da dışarıdan metin olarak gelmiştir.
const oran = "56.25"console.log(oran.toFixed(1))C:\kod\ogren\ders9.js:2console.log(oran.toFixed(1)) ^ TypeError: oran.toFixed is not a function at Object.<anonymous> (C:\kod\ogren\ders9.js:2:18) ... Node.js v24.20.0Nasıl düzelir? Önce sayıya çevir: Number(oran).toFixed(1). Bu hatanın bir başka sebebi toFixed'i iki kez çağırmaktır; ilk toFixed zaten metin döndürür.
TypeError: isim.toUppercase is not a functionNe zaman çıkar? Metot adını yanlış yazınca. Metot adları camelCase'tir ve büyük-küçük harfe duyarlıdır: toUpperCase doğru; toUppercase, touppercase, ToUpperCase yanlış. Aynı hata Number.isNan (doğrusu isNaN) ve Math.Round (doğrusu round) için de çıkar.
const isim = "Teemo"console.log(isim.toUppercase())C:\kod\ogren\ders9.js:2console.log(isim.toUppercase()) ^ TypeError: isim.toUppercase is not a function at Object.<anonymous> (C:\kod\ogren\ders9.js:2:18) ... Node.js v24.20.0Nasıl düzelir? Adı harfi harfine yaz. Ders 5'teki takim.Push hatasının metindeki karşılığı: nokta koyduğunda VS Code'un önerdiği listeden seçersen yazım hatası yapmazsın. Math'i küçük harfle yazarsan (math.round) mesaj bu kez ReferenceError: math is not defined olur.
SyntaxError: Unexpected end of input (kapanmamış ters tırnak)Ne zaman çıkar? Ters tırnağı açıp kapatmayı unutunca. Çift tırnaklı metin satır sonunda biter; ters tırnaklı metin ise çok satırlı olabildiği için JavaScript kapanışı dosyanın sonuna kadar arar, bulamayınca "girdi beklenmedik yerde bitti" der.
const isim = "Ahri"console.log(`Merhaba ${isim})C:\kod\ogren\ders9.js:2console.log(`Merhaba ${isim}) SyntaxError: Unexpected end of input at wrapSafe (node:internal/modules/cjs/loader:1861:18) ... Node.js v24.20.0Son satırdan sonra Enter'a bastıysan mesaj dosyanın en sondaki boş satırını gösterir: ders9.js:3 ve altında boş bir kod satırı. Dosyada aşağıda başka bir ters tırnak varsa mesaj yine değişir, çünkü JavaScript metni o ters tırnağa kadar uzatır:
const isim = "Ahri"console.log(`Merhaba ${isim})console.log(`Hoş geldin`)C:\kod\ogren\ders9.js:2console.log(`Merhaba ${isim}) SyntaxError: missing ) after argument list at wrapSafe (node:internal/modules/cjs/loader:1861:18) ... Node.js v24.20.0Nasıl düzelir? Mesajlar ters tırnaktan söz etmez, ^ da göstermez; Ders 2'deki eksik süslü parantezde olduğu gibi, gösterilen satır sorunun başladığı yer olmayabilir. Renklere bak: kapanmamış şablondan sonraki kod bir sonraki ters tırnağa kadar metin rengine boyanır (ikinci örnekte 3. satırdaki console.log( gibi). Oradan geriye doğru ilk ters tırnağı bul ve kapat.
${kda} olduğu gibi ekrana çıkıyorNe zaman çıkar? Yuvayı çift ya da tek tırnaklı metnin içine yazınca. Hata mesajı çıkmaz; program çalışır ama yanlış yazar.
const kda = "8.00"console.log("KDA: ${kda}")KDA: ${kda}Nasıl düzelir? Tırnakları ters tırnakla değiştir: `KDA: ${kda}`. Türkçe Q klavyede AltGr + ,, ardından Boşluk.
NaN çıkıyor ama hata mesajı yokNe zaman çıkar? Hesaba sayıya çevrilemeyen bir metin ya da undefined girince: yanlış yazılmış bir obje alanı, split'ten beklenenden az parça, virgüllü bir sayı ("3,5").
const parcalar = "7-2-9".split("/")const kill = Number(parcalar[0])const asist = Number(parcalar[2])console.log(kill + asist)NaNNasıl düzelir? Geriye doğru iz sür: hesaba giren her değeri console.log ile yazdır (Ders 7). Burada parcalar dizisi [ '7-2-9' ] çıkar; skorun ayırıcısı / değil -. Dışarıdan gelen her değeri çevirdikten hemen sonra Number.isNaN ile denetlersen sorunu kaynağında yakalarsın.
TypeError: Cannot read properties of undefined (reading 'trim')Ne zaman çıkar? split beklediğinden az parça verince, olmayan parçanın metodunu çağırırsan. Olmayan parça undefined'dır ve undefined'ın ne alanı ne metodu vardır (Ders 5'te enYuksek.isim ile görmüştün).
const parcalar = "Ahri".split(":")console.log(parcalar[1].trim())C:\kod\ogren\ders9.js:2console.log(parcalar[1].trim()) ^ TypeError: Cannot read properties of undefined (reading 'trim') at Object.<anonymous> (C:\kod\ogren\ders9.js:2:25) ... Node.js v24.20.0Nasıl düzelir? Metinde : olmadığı için dizinin tek elemanı var, parcalar[1] yok. Parçaları kullanmadan önce sayısını denetle (parcalar.length === 2) ya da bölmeden önce includes(":") ile ayırıcının varlığına bak.
Ne zaman çıkar? Metodun sonucunu bir yere koymayınca. Hata mesajı yok, metin eski hâliyle kalır.
let isim = " teemo "isim.trim()isim.toUpperCase()console.log(`[${isim}]`)[ teemo ]Nasıl düzelir? Metin metotları yeni metin döndürür. Sonucu kutuya geri koy: isim = isim.trim().toUpperCase().
[Function: toUpperCase] yazıyorNe zaman çıkar? Metodu parantezsiz yazınca. Parantez yoksa metot çalışmaz; JavaScript sana metodun kendisini gösterir. Ders 3'teki parantezsiz fonksiyon çağrısının aynısı.
const isim = "Teemo"console.log(isim.toUpperCase)[Function: toUpperCase]Nasıl düzelir? Parantezi ekle: isim.toUpperCase(). length ise tersine parantezsiz yazılır; o bir metot değil, alan.
İ ve i)Ne zaman çıkar? Türkçe metni toLowerCase ya da toUpperCase ile çevirip karşılaştırınca. "İ" küçültülünce iki karakterli bir i̇ olur, "i" büyütülünce noktasız I olur.
console.log("İKSİR".toLowerCase() === "iksir")console.log("İKSİR".toLocaleLowerCase("tr") === "iksir")falsetrueNasıl düzelir? Türkçe metinde toLocaleLowerCase("tr") ve toLocaleUpperCase("tr") kullan; parantezdeki "tr"yi unutma.
Görevler
Görevleri, başka bir dosya söylenmedikçe ders9.js'te yap; önceki kodu sil ya da yorum satırı yap. Önce kendin dene, takılırsan ipuçlarını sırayla aç. Çözüme en son bak.
Şampiyon kartı
Şu değişkenlerle başla:
const isim = "Lux"const unvan = "ışığın leydisi"const mana = 320const maxMana = 500Tek bir console.log ve tek bir şablon metinle şu iki satırı yazdır:
IŞIĞIN LEYDİSİLux - Mana 320/500 (%64)Yüzdeyi elle yazmak yok; en yakın tam sayıya yuvarlanmış olarak JavaScript hesaplasın. Bitince mana'yı 0, 500 ve 333 yapıp dene: %0, %100 ve %67 çıkmalı.
İpucu 1
Unvanda ı ve i var. Hangi metot Türkçe harfleri doğru büyütüyordu?
İpucu 2
Yüzde mana / maxMana * 100; en yakına yuvarlamak için Math.round. Hepsini ${ } yuvasının içine yazabilirsin.
İpucu 3
İki satır için şablon metnin içinde gerçekten alt satıra geç. İkinci satırı en baştan yaz; girintisiz.
Çözümü göster
const isim = "Lux"const unvan = "ışığın leydisi"const mana = 320const maxMana = 500console.log(`${unvan.toLocaleUpperCase("tr")}${isim} - Mana ${mana}/${maxMana} (%${Math.round(mana / maxMana * 100)})`)IŞIĞIN LEYDİSİLux - Mana 320/500 (%64)toUpperCase kullansaydın ilk satır IŞIĞIN LEYDISI çıkardı. 333 için gerçek yüzde 66.6'dır; Math.round onu 67 yapar.
Saldırı hızı
Bir şampiyonun saldırı hızı, istatistik ekranından metin olarak okunuyor: const ekran = "0.7 saldırı/sn". Hızı bu metinden sayı olarak al. Aldığı her eşya hızı 0.1 artırıyor; bir for döngüsüyle üç eşya ekle. Sonra hızı iki basamakla yazdır ve hız en az 1 ise Hızlı saldırı açıldı, değilse Henüz yavaş yazdır:
Saldırı hızı: 1.00Hızlı saldırı açıldıÖnce yuvarlama yapmadan dene ve ne olduğuna bak. Sonra düzelt.
İpucu 1
Number(ekran) burada NaN verir. Ondalıklı sayıyı okuyup arkasındaki yazıyı umursamayan çevirici hangisiydi?
İpucu 2
Yuvarlamadan çalıştırınca ekranda 1.00 yazıp ardından Henüz yavaş geliyorsa sebebi bu dersin "Ondalık tuzağı" bölümünde.
İpucu 3
Karşılaştırmadan önce yuvarla: hiz = Math.round(hiz * 100) / 100. Ekrana yazarken hiz.toFixed(2).
Çözümü göster
const ekran = "0.7 saldırı/sn"let hiz = parseFloat(ekran)for (let i = 0; i < 3; i++) { hiz = hiz + 0.1}hiz = Math.round(hiz * 100) / 100console.log(`Saldırı hızı: ${hiz.toFixed(2)}`)if (hiz >= 1) { console.log("Hızlı saldırı açıldı")} else { console.log("Henüz yavaş")}Saldırı hızı: 1.00Hızlı saldırı açıldıparseInt kullansaydın küsurat atılır, hız 0'dan başlardı. Yuvarlama satırını silersen hiz 0.9999999999999999 olur: ekranda toFixed(2) sayesinde 1.00 görünür ama hiz >= 1 false verir ve Henüz yavaş yazılır. Ekranda doğru görünen bir değer karşılaştırmada yanlış çıkabilir. Sınır denemesi: döngüyü 2 tura indir; 0.90 ve Henüz yavaş çıkmalı.
Skor tablonu hizala
Ders 8'de yazdığın skor.js'i aç. Tablosu şöyle çıkıyordu; ad uzadıkça arkasındaki her şey kayıyor:
Garen 4/6/5: 5 puan (Orta)Lux 2/3/14: 14 puan (Orta)Jinx 11/2/6: 35 puan (Yıldız)Teemo 0/9/3: -15 puan (Zor maç)Ahri 7/4/7: 20 puan (İyi)skorTablosu fonksiyonunu bu dersteki araçlarla düzenle: + ile kurulan satırları şablon metne çevir, başlığın altına bir sütun başlıkları satırı ekle ve sütunları hizala. İsim ve skor 8'er karakterlik sütunlara sola, puan 5 karakterlik sütuna sağa yaslansın; unvan iki boşluk sonra gelsin:
--- Skor tablosu ---İsim Skor Puan UnvanGaren 4/6/5 5 OrtaLux 2/3/14 14 OrtaJinx 11/2/6 35 YıldızTeemo 0/9/3 -15 Zor maçAhri 7/4/7 20 İyiTakım puanı: 59MVP: Jinx (35 puan)Maç verisi yokskor.js elinde yoksa Ders 8'deki "Skor tablosu" görevinin çözümünü yaz, sonra düzenle. Dosyanın sonunda Ders 8'in sınır denemesi satırları duruyorsa şimdilik başlarına // koy; beklenen çıktı yalnızca iki skorTablosu çağrısınınki.
İpucu 1
Puan bir sayı, padStart ise metin metodu: önce String(p).
İpucu 2
Sütun başlıkları satırı da aynı genişliklerle kurulur: "İsim".padEnd(8), "Skor".padEnd(8), "Puan".padStart(5).
İpucu 3
Oyuncu satırı dört parçadan oluşur: o.isim.padEnd(8), kda.padEnd(8), String(p).padStart(5) ve iki boşluktan sonra unvan(p). Hepsini tek bir şablon metnin yuvalarına koy. kda kutusunu da şablon metinle kur: `${o.kill}/${o.olum}/${o.asist}`.
Çözümü göster
const oyuncular = [ { isim: "Garen", kill: 4, olum: 6, asist: 5 }, { isim: "Lux", kill: 2, olum: 3, asist: 14 }, { isim: "Jinx", kill: 11, olum: 2, asist: 6 }, { isim: "Teemo", kill: 0, olum: 9, asist: 3 }, { isim: "Ahri", kill: 7, olum: 4, asist: 7 }] function puan(oyuncu) { return oyuncu.kill * 3 + oyuncu.asist - oyuncu.olum * 2} function unvan(p) { if (p >= 30) { return "Yıldız" } else if (p >= 15) { return "İyi" } else if (p >= 0) { return "Orta" } else { return "Zor maç" }} function skorTablosu(liste) { if (liste.length === 0) { console.log("Maç verisi yok") return } console.log("--- Skor tablosu ---") console.log(`${"İsim".padEnd(8)}${"Skor".padEnd(8)}${"Puan".padStart(5)} Unvan`) let toplam = 0 let mvp = liste[0] for (const o of liste) { const p = puan(o) const kda = `${o.kill}/${o.olum}/${o.asist}` console.log(`${o.isim.padEnd(8)}${kda.padEnd(8)}${String(p).padStart(5)} ${unvan(p)}`) toplam = toplam + p if (p > puan(mvp)) { mvp = o } } console.log(`Takım puanı: ${toplam}`) console.log(`MVP: ${mvp.isim} (${puan(mvp)} puan)`)} skorTablosu(oyuncular)skorTablosu([])--- Skor tablosu ---İsim Skor Puan UnvanGaren 4/6/5 5 OrtaLux 2/3/14 14 OrtaJinx 11/2/6 35 YıldızTeemo 0/9/3 -15 Zor maçAhri 7/4/7 20 İyiTakım puanı: 59MVP: Jinx (35 puan)Maç verisi yokVurgulu satırlar değişenler. Eksi puan (-15) da sağa yaslandı, çünkü padStart eksi işaretini de bir karakter sayar. Sınır denemesi: listeye 8 karakterden uzun adlı bir oyuncu ekle ("KassadinUstasi"); o satır kayar, çünkü padEnd uzun metni kısaltmaz. Uzun adları slice ile kısaltmayı bir sonraki görevde yapacaksın.
Riot ID
Riot ID'ler Ad#ETİKET biçimindedir. Bir riotId metni için şu kuralları uygulayan bir program yaz:
- Baştaki ve sondaki boşlukları at.
- Metinde
#yoksaGeçersiz Riot IDyaz ve dur. - Ad 8 karakterden uzunsa ilk 8 karakterini al, sonuna
...ekle. - Etiketi büyük harfe çevir. Etiket
TRile başlıyorsa satırın sonuna bir boşluk ve(Türkiye)ekle.
riotId | Beklenen çıktı |
|---|---|
" Faker#kr1 " | Faker #KR1 |
"KassadinUstasi#tr1" | Kassadin... #TR1 (Türkiye) |
"Annie123#tr1" | Annie123 #TR1 (Türkiye) |
"Teemo" | Geçersiz Riot ID |
İpucu 1
Sıra: önce trim, sonra includes("#") ile denetim, en son split("#"). split'in verdiği dizinin 0. elemanı ad, 1. elemanı etiket.
İpucu 2
Adı kısaltabilmek için onu let ile tut: if (ad.length > 8) { ad = ad.slice(0, 8) + "..." }. Annie123 tam 8 karakter; kısaltılmamalı.
İpucu 3
Etiketler İngilizce harflerden oluşur, bu yüzden düz toUpperCase doğru seçim. Sonuç satırını let ile kur, gerekirse etiket.startsWith("TR") ile sonuna ekle.
Çözümü göster
const riotId = " Faker#kr1 "const temiz = riotId.trim()if (!temiz.includes("#")) { console.log("Geçersiz Riot ID")} else { const parcalar = temiz.split("#") let ad = parcalar[0] const etiket = parcalar[1].toUpperCase() if (ad.length > 8) { ad = ad.slice(0, 8) + "..." } let satir = `${ad} #${etiket}` if (etiket.startsWith("TR")) { satir = satir + " (Türkiye)" } console.log(satir)}Faker #KR1Tablodaki bütün satırları tek tek dene. Bir sınır daha: "#tr1" girersen ad boş kalır ve program başında boşluk olan bir #TR1 (Türkiye) satırı yazar. Çalışıyor ama doğru değil; ad.length === 0 durumunu da geçersiz saymak için koşulu || ile genişletebilirsin.
TFT faizi
TFT'de her turun başında elindeki her 10 altın için 1 altın faiz alırsın, ama en fazla 5. Altın miktarı ekrandan metin olarak okunuyor: const ekran = "47 altın". Faizi ve tam faize, yani 50 altına kaç tur kaldığını hesapla: her tur 5 altın kazandığını, hiç harcamadığını düşün ve faizi tur hesabına katma. Metnin başında sayı yoksa Altın okunamadı yaz.
ekran | Beklenen çıktı |
|---|---|
"47 altın" | Altın: 47, faiz: 4, tam faize 1 tur |
"9 altın" | Altın: 9, faiz: 0, tam faize 9 tur |
"10 altın" | Altın: 10, faiz: 1, tam faize 8 tur |
"50 altın" | Altın: 50, faiz: 5, tam faize 0 tur |
"120 altın" | Altın: 120, faiz: 5, tam faize 0 tur |
"altın yok" | Altın okunamadı |
İpucu 1
Altın tam sayıdır ve arkasından yazı geliyor. Hangi çevirici tam sayıyı okuyup gerisini umursamıyordu? Okuduktan hemen sonra Number.isNaN ile denetle.
İpucu 2
Faiz için önce kaç tam 10 olduğunu bul (Math.floor). "En fazla 5" bir tavandır: Math.min(5, …).
İpucu 3
Tur sayısı bir "kaç tane gerekir?" sorusu: eksik altını 5'e böl ve Math.ceil ile yukarı yuvarla. 50'yi geçince eksik altın eksiye düşmesin: Math.max(0, 50 - altin).
Çözümü göster
const ekran = "47 altın"const altin = parseInt(ekran)if (Number.isNaN(altin)) { console.log("Altın okunamadı")} else { const faiz = Math.min(5, Math.floor(altin / 10)) const eksik = Math.max(0, 50 - altin) const tur = Math.ceil(eksik / 5) console.log(`Altın: ${altin}, faiz: ${faiz}, tam faize ${tur} tur`)}Altın: 47, faiz: 4, tam faize 1 turNumber("47 altın") NaN verirdi; burada parseInt doğru seçim. Tur hesabında floor kullansaydın 47 altında 0 tur çıkardı, oysa 3 altın eksik; Math.max olmasaydı 120 altında -14 tur görürdün. Sınırlar: 9–10 (faiz), 44–45 (tur), 50–120 (tavan). "0 altın" için faiz: 0 ve 10 tur çıkmalı.
Hasar raporu
Son maçlarındaki hasar değerleri noktalı virgülle ayrılmış tek bir metin olarak geldi. Bazı kayıtlar boş, biri bozuk, bazılarında fazladan boşluk var:
const kayit = "18250; 9400;;23110 ;abc"Geçerli her maçı hasarı sağa yaslanmış olarak listele, geçersiz kayıtları say, ortalamayı tek basamakla ve Türkçe virgülle, en yüksek hasarı da en sonda yazdır:
Maç 1: 18250Maç 2: 9400Maç 3: 23110Geçersiz kayıt: 2Ortalama: 16920,0En yüksek: 23110Hiç geçerli maç yoksa (örneğin ";;abc") ortalama ve en yüksek yerine Geçerli maç yok yazmalı.
İpucu 1
split(";") ile böl, her parçayı trim et ve Number ile çevir. Dikkat: boş parça "" olur ve Number("") 0 verir; boşları ayrıca yakalaman gerek.
İpucu 2
Döngüden önce dört kutu aç: geçerli sayısı, geçersiz sayısı, toplam, en yüksek. En yükseği enYuksek = Math.max(enYuksek, hasar) ile güncelleyebilirsin. Hasarı sağa yaslamak için String(hasar).padStart(6).
İpucu 3
Ortalama: (toplam / gecerli).toFixed(1).replace(".", ","). gecerli 0 iken bu satıra hiç gelme; yoksa 0 / 0 sonucu NaN olur.
Çözümü göster
const kayit = "18250; 9400;;23110 ;abc"const parcalar = kayit.split(";")let gecerli = 0let gecersiz = 0let toplam = 0let enYuksek = 0 for (const parca of parcalar) { const temiz = parca.trim() const hasar = Number(temiz) if (temiz === "" || Number.isNaN(hasar)) { gecersiz++ } else { gecerli++ toplam = toplam + hasar enYuksek = Math.max(enYuksek, hasar) console.log(`Maç ${gecerli}: ${String(hasar).padStart(6)}`) }} console.log(`Geçersiz kayıt: ${gecersiz}`)if (gecerli === 0) { console.log("Geçerli maç yok")} else { const ortalama = (toplam / gecerli).toFixed(1).replace(".", ",") console.log(`Ortalama: ${ortalama}`) console.log(`En yüksek: ${enYuksek}`)}Maç 1: 18250Maç 2: 9400Maç 3: 23110Geçersiz kayıt: 2Ortalama: 16920,0En yüksek: 23110kayit'i ";;abc" yap: Geçersiz kayıt: 3 ve Geçerli maç yok çıkmalı. "0" tek başına geçerli bir maçtır (Ortalama: 0,0); temiz === "" denetimi olmasaydı boş kayıtlar da sıfır hasarlı maç sayılırdı. "-5" gibi eksi bir hasarı da şu an kabul ediyor; istersen onu da geçersiz say.
Kendini sına
Soru 1
Bu program ne yazar?
const isim = "Jinx"console.log("Merhaba ${isim}")console.log(`Merhaba ${isim}`)Cevabı göster
İlk satır Merhaba ${isim} yazar: çift tırnağın içinde yuva çalışmaz, yazı harfi harfine basılır. İkinci satır Merhaba Jinx yazar.
Soru 2
"Sihirdar".slice(2, 5) ne döndürür?
Cevabı göster
"hir". Kesim çizgilerini düşün: 2 numaralı çizgiden başla, 5 numaralı çizgide dur. Arada 2, 3 ve 4 numaralı karakterler var: h, i, r. Bitiş (5) dahil değil.
Soru 3
Bu üç satır ne yazar?
console.log(Number("15 asist"))console.log(parseInt("15 asist"))console.log(Number(""))Cevabı göster
NaN, 15 ve 0. Number metnin tamamının sayı olmasını ister. parseInt baştan okur ve sayı olmayan ilk karakterde durur. Boş metni ise Number 0 sayar; boş girdiyi ayrıca denetlemen gerekmesinin sebebi bu.
Soru 4
Hangi satır hata verir? Mesajı ne olur?
const kill = 12const metin = "12"console.log(metin.padStart(4))console.log(kill.padStart(4))Cevabı göster
Hata son satırda çıkar: TypeError: kill.padStart is not a function. padStart bir metin metodu; kill ise bir sayı. Bir önceki satır sorunsuz çalışır: "12"'nin başına iki boşluk ekleyip onu dört karaktere tamamlar. Düzeltmek için String(kill).padStart(4).
Soru 5
Math.round(4.5), Math.floor(4.5), Math.ceil(4.1) ve Math.abs(-4) kaçtır?
Cevabı göster
5, 4, 5 ve 4. round en yakına gider ve tam ortadaki .5'i yukarı yuvarlar. floor her zaman aşağı, ceil her zaman yukarı yuvarlar. abs eksi işaretini atar.
Soru 6
Bu kod neden hiçbir zaman "Geçersiz" yazmaz? Nasıl düzeltirsin?
const deger = Number("abc")if (deger === NaN) { console.log("Geçersiz")}Cevabı göster
NaN hiçbir şeye eşit değildir, kendisine bile; deger === NaN her zaman false'tur. Doğrusu if (Number.isNaN(deger)).
Soru 7
"İKSİR".toLowerCase() ile "İKSİR".toLocaleLowerCase("tr") ekranda neredeyse aynı görünür. Aynı mıdır?
Cevabı göster
Değildir. İlkinde her İ, bir i ve ayrı bir nokta işaretinden oluşan iki karaktere dönüşür: sonuç 7 karakter, ikincisi ise 5 karakterlik düz iksir. Bu yüzden "İKSİR".toLowerCase() === "iksir" false verir; Türkçe metinde "tr" ile çevir.
Soru 8
0.1 + 0.2 === 0.3 neden false? İki sayıyı nasıl karşılaştırırdın?
Cevabı göster
Bilgisayar 0.1 ve 0.2'yi ikilik sistemde tam olarak saklayamaz; toplamları 0.30000000000000004 çıkar. Karşılaştırmadan önce iki tarafı aynı basamağa yuvarla: Math.round((0.1 + 0.2) * 100) / 100 === 0.3 true verir.
Kayıt noktası
Bu derste bir dosya ekledin (ders9.js), bir dosyayı da değiştirdin (Görev 3'teki skor.js). VS Code terminalinde, C:\kod\ogren klasöründeyken önce neyin değiştiğine bak, sonra kaydet ve GitHub'a gönder:
Bir sonraki derste fonksiyonları derinlemesine ele alacaksın: ok fonksiyonları, varsayılan parametreler ve bir fonksiyonu başka bir fonksiyona vermek.