İçeriğe geç
XDD.baby Yazılım Öğrenme
Sözlük Hata rehberi 0/56

Blok 2 · Ders 12

Obje ve dizilerde ileri teknikler

Parçalama (destructuring), yayma (...), Object.keys/values/entries, kopya ile referans farkı, JSON ve null güvenliği.

Bu dersin sonunda

  • Bir objeyi ya da diziyi parçalayarak (destructuring) değerleri tek satırda kendi kutularına alabilirsin
  • Yayma (...) ile dizi ve objeleri kopyalayıp birleştirebilir, geri kalanları (rest) tek adda toplayabilirsin
  • Object.keys, Object.values ve Object.entries ile bir objenin bütün alanlarını gezebilirsin
  • Kopya ile referansı ayırt edip asıl veriyi structuredClone ile koruyabilirsin
  • JSON.stringify ve JSON.parse ile objeyi metne, metni objeye çevirebilirsin
  • null, undefined ve yanlış sayılan değerlerin tuzaklarından ?. ve ?? ile kaçınabilirsin
Bu sayfada
  1. Parçalama: objeyi kutulara açmak
  2. Dizi parçalama ve değiş tokuş
  3. Yayma: dök, kopyala, birleştir
  4. Geri kalanı toplamak: rest
  5. Objenin bütün alanlarını gezmek
  6. Kopya mı, aynı obje mi?
  7. Sığ kopya tuzağı ve structuredClone
  8. JSON: objeyi metne, metni objeye çevirmek
  9. null ve undefined: iki çeşit "yok"
  10. Doğru sayılanlar ve yanlış sayılanlar
  11. Güvenli okuma: ?.
  12. Yedek değer: ??
  13. Sık hatalar
  14. Görevler
  15. Kendini sına
  16. Kayıt noktası

Ders 4'te objeyi, Ders 5'te diziyi tanıdın, Ders 11'de dizileri metotlarla dönüştürdün. Bu derste ikisini daha rahat ve daha güvenli kullanmayı öğreneceksin: bir objeyi tek satırda kutulara açmak, kopyalayıp birleştirmek, bütün alanlarını gezmek, metne çevirip saklamak ve olmayan bir değer yüzünden çökmemek.

Bu derste göreceğin const { isim } = oyuncu, { ...ayarlar } ya da oyuncu.klan?.ad ?? "Klansız" gibi satırlar gerçek projelerde neredeyse her dosyada karşına çıkar. Bütün örnekleri C:\kod\ogren\ders12.js dosyasına yaz; her yeni örnekten önce eski kodu sil ya da satırların başına // koy.

Parçalama: objeyi kutulara açmak

Bir objenin birkaç alanıyla çalışacaksan her birini ayrı bir değişkene almak istersin. Şimdiye kadar bunu tek tek yaptın:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }const hp = ahri.hpconst gold = ahri.goldconsole.log(hp, gold)
Çıktı
100 300

Her satırda aynı adı iki kez yazıyorsun: hp = ahri.hp. Alan sayısı arttıkça bu tekrar hem yorar hem de yazım hatasına kapı açar. JavaScript'in bunun için kısa bir yazımı var:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }const { hp, gold } = ahriconsole.log(hp, gold)console.log(ahri)
Çıktı
100 300{ isim: 'Ahri', hp: 100, gold: 300 }

İkinci satırı şöyle oku: "ahri objesinden hp ve gold alanlarını al, aynı adlı iki kutuya koy." Süslü parantez burada obje kurmuyor; eşittirin solunda durduğu için "bu objeyi aç" anlamına geliyor. Buna parçalama (destructuring) denir.

Son satıra da bak: obje yerinde duruyor. Parçalama değerleri kopyalar, objeden söküp almaz.

const { hp, gold } = ahri ahri (obje) isim: "Ahri" alınmadı hp: 100 gold: 300 hp 100 gold 300 obje yerinde kalır iki yeni kutu
Şekil 12.1 Parçalama, objedeki alanların değerini aynı adlı yeni kutulara kopyalar. Obje olduğu gibi kalır.

İki kural var. Süslü parantezin içindeki adlar objedeki anahtarlarla aynı olmalı; sıra önemli değil, const { gold, hp } = ahri de aynı işi yapar. Objede olmayan bir adı istersen kutuya undefined konur, hata çıkmaz:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }const { mana, gold } = ahriconsole.log(mana, gold)
Çıktı
undefined 300

Başka bir adla almak

Aynı alanı iki ayrı objeden almak istediğinde sorun çıkar. Ahri'nin de Garen'in de hp alanı var:

ders12.js
const ahri = { isim: "Ahri", hp: 100 }const garen = { isim: "Garen", hp: 620 }const { hp } = ahriconst { hp } = garen
Hata çıktısı
C:\kod\ogren\ders12.js:4const { hp } = garen        ^ SyntaxError: Identifier 'hp' has already been declared    at wrapSafe (node:internal/modules/cjs/loader:1861:18)    ... Node.js v24.20.0

Bu, Ders 1'den tanıdığın hata: aynı adla iki kutu açılamaz. Çözüm, alanı alırken kutuya yeni bir ad vermek. Yazımı alan: yeniAd:

ders12.js
const ahri = { isim: "Ahri", hp: 100 }const garen = { isim: "Garen", hp: 620 }const { hp: ahriHp } = ahriconst { hp: garenHp } = garenconsole.log(ahriHp, garenHp)
Çıktı
100 620

Varsayılan değer

Ders 10'da parametrelere varsayılan değer vermiştin. Parçalamada da aynı = ile, alan objede yoksa kullanılacak değeri yazarsın:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }const { seviye = 1, hp = 50 } = ahriconsole.log(seviye, hp)
Çıktı
1 100

seviye objede yok, varsayılan 1 kullanıldı. hp var; varsayılan 50 görmezden gelindi. Yeni ad ile varsayılanı birlikte de yazabilirsin. Aşağıdaki dört yazımın hepsi const … = ahri kalıbında eşittirin soluna gelir:

Eşittirin soluAnlamı
{ hp }hp alanını hp kutusuna al
{ hp: can }hp alanını can adlı kutuya al
{ hp = 50 }hp alanını al; yoksa 50 kullan
{ hp: can = 50 }hp alanını can kutusuna al; yoksa 50 kullan

Fonksiyonun parantezinde parçalama

Parçalamayı en çok fonksiyon parametrelerinde göreceksin. Bu fonksiyon bir obje alıyor ama içinden yalnız iki alana ihtiyacı var:

ders12.js
function kartYaz({ isim, hp }) {    console.log(`${isim}: ${hp} HP`)} const ahri = { isim: "Ahri", hp: 100, gold: 300 }kartYaz(ahri)kartYaz({ isim: "Lux", hp: 90 })
Çıktı
Ahri: 100 HPLux: 90 HP

Gelen obje parantezin içinde hemen açılır; fonksiyonun içinde sampiyon.isim yerine doğrudan isim yazarsın. Ders 4'te her alanın önüne sampiyon. yazmanın satırları uzattığını görmüştün; kısaltmanın yolu buydu. Blok 6'da React'le arayüz yazarken bu kalıbı neredeyse her dosyada göreceksin.

Sık hataTypeError: Cannot destructure property 'isim' of 'undefined' as it is undefined.

Ne zaman çıkar? Parçalanacak şey undefined olduğunda. En sık hâli: objeyi parçalayan bir fonksiyonu argümansız çağırmak.

ders12.js
function kartYaz({ isim, hp }) {    console.log(`${isim}: ${hp} HP`)} kartYaz()
Hata çıktısı
C:\kod\ogren\ders12.js:1function kartYaz({ isim, hp }) {                   ^ TypeError: Cannot destructure property 'isim' of 'undefined' as it is undefined.    at kartYaz (C:\kod\ogren\ders12.js:1:20)    at Object.<anonymous> (C:\kod\ogren\ders12.js:5:1)    ... Node.js v24.20.0

Mesaj "undefined'ın isim alanını (property) parçalayamam, çünkü o undefined" diyor. Dikkat: en üstteki satır numarası (1) fonksiyonun tanımını gösteriyor. Hatayı tetikleyen çağrıyı ikinci at satırında bulursun: ders12.js:5. Bu satırları okumayı Ders 13'te ayrıntılı göreceksin.

Nasıl düzelir? Fonksiyonu bir objeyle çağır. Argüman gelmeyebilecekse parametreye = {} varsayılanı ver, alanlara da kendi varsayılanlarını yaz:

ders12.js
function kartYaz({ isim = "Bilinmeyen", hp = 0 } = {}) {    console.log(`${isim}: ${hp} HP`)} kartYaz({ isim: "Lux", hp: 90 })kartYaz({ isim: "Teemo" })kartYaz()
Çıktı
Lux: 90 HPTeemo: 0 HPBilinmeyen: 0 HP

Son çağrıda argüman gelmedi, parametre = {} sayesinde boş obje oldu; boş objede olmayan isim ve hp de kendi varsayılanlarını aldı.

Aynı hata bir değişkeni parçalarken de çıkar; o zaman mesajda değişkenin adı yazar: Cannot destructure property 'isim' of 'oyuncu' as it is undefined.

Dizi parçalama ve değiş tokuş

Dizileri de parçalayabilirsin. Dizide ad yok, sıra var; bu yüzden köşeli parantez kullanılır ve kutular sırayla dolar:

ders12.js
const siralama = ["Jinx", "Lux", "Garen", "Teemo"]const [birinci, ikinci] = siralamaconsole.log(birinci, ikinci)
Çıktı
Jinx Lux

İlk kutu 0. elemanı, ikinci kutu 1. elemanı alır; adları sen seçersin. Atlamak istediğin elemanın yerine yalnız virgülünü yazarsın. Dizide olmayan bir sıraya undefined düşer:

ders12.js
const siralama = ["Jinx", "Lux", "Garen"]const [, , ucuncu, dorduncu] = siralamaconsole.log(ucuncu, dorduncu)
Çıktı
Garen undefined

Değiş tokuş

İki değişkenin değerini takas etmek sık gerekir: iki oyuncunun koridorunu ya da iki eşyanın yerini değiştirmek gibi. Eskiden üçüncü, geçici bir kutu gerekirdi:

ders12.js
let mavi = "Ahri"let kirmizi = "Zed"let gecici = mavimavi = kirmizikirmizi = geciciconsole.log(mavi, kirmizi)
Çıktı
Zed Ahri

Dizi parçalamayla tek satır yeter:

ders12.js
let mavi = "Ahri"let kirmizi = "Zed";[mavi, kirmizi] = [kirmizi, mavi]console.log(mavi, kirmizi)
Çıktı
Zed Ahri

Üçüncü satır sağdan sola çalışır: önce [kirmizi, mavi] ile ["Zed", "Ahri"] dizisi kurulur, sonra soldaki parçalama bu diziyi sırayla mavi ve kirmizi kutularına dağıtır. Satırda let yok, çünkü kutular zaten var; yalnız içlerini değiştiriyorsun. Bu yüzden kutular let ile açılmış olmalı, const olsaydı Assignment to constant variable. hatası alırdın.

Satırın başındaki ; bir süs değil. Ders 7'de noktalı virgül koymamanın serbest olduğunu gördün, ama bunun bir istisnası var:

Sık hataSatır [ ile başlayınca önceki satıra yapışır

Ne zaman çıkar? Noktalı virgül kullanmadığın kodda bir satır [ ile başladığında. JavaScript satır sonunu her zaman "komut bitti" saymaz; sonraki satır [ ile başlıyorsa onu önceki satırın devamı sanar.

ders12.js
let mavi = "Ahri"let kirmizi = "Zed"[mavi, kirmizi] = [kirmizi, mavi]console.log(mavi, kirmizi)
Hata çıktısı
C:\kod\ogren\ders12.js:3[mavi, kirmizi] = [kirmizi, mavi]       ^ ReferenceError: Cannot access 'kirmizi' before initialization    at Object.<anonymous> (C:\kod\ogren\ders12.js:3:8)    ... Node.js v24.20.0

JavaScript 2. ve 3. satırı tek komut olarak okudu: let kirmizi = "Zed"[mavi, kirmizi] = …. Yani kirmizi kutusu daha açılırken içinde kirmizi'yi kullanmaya kalktı; mesaj "kirmizi'ye ilk değeri verilmeden erişilemez" diyor.

Önceki satır başka bir şey olsaydı mesaj da değişirdi: console.log(...) satırından sonra TypeError: Cannot set properties of undefined (setting 'Zed') çıkar. Mesajlar kafa karıştırır, çünkü JavaScript senin yazmadığın bir komutu çalıştırmaya çalışıyor.

Nasıl düzelir? [ ile başlayan satırın başına ; koy: ;[mavi, kirmizi] = [kirmizi, mavi]. Noktalı virgülsüz yazanların akılda tuttuğu kural budur: satır [, ( ya da ters tırnak (`) ile başlıyorsa başına ; konur.

Yayma: dök, kopyala, birleştir

Diziye eleman eklemek için Ders 5'te push kullandın. push asıl diziyi değiştirir. Çoğu zaman eski hâli de saklamak istersin: maç başındaki kadroyu ve son kadroyu ayrı ayrı göstermek gibi. Yeni bir dizi kurup eskisinin elemanlarını içine dökersen asıl diziye dokunmamış olursun:

ders12.js
const takim = ["Ahri", "Garen"]const yeniTakim = [...takim, "Lux"]console.log(takim)console.log(yeniTakim)
Çıktı
[ 'Ahri', 'Garen' ][ 'Ahri', 'Garen', 'Lux' ]

...takim şu demek: "takim'ın elemanlarını buraya tek tek dök." Köşeli parantez yeni bir dizi kurar; içine önce takim'ın elemanları, sonra "Lux" girer. takim olduğu gibi kaldı. Bu üç noktaya yayma (spread) denir.

Farkı kendin gör:

ders12.js
const mavi = ["Ahri", "Garen"]const kirmizi = ["Zed", "Jinx"]console.log([mavi, kirmizi])console.log([...mavi, ...kirmizi])
Çıktı
[ [ 'Ahri', 'Garen' ], [ 'Zed', 'Jinx' ] ][ 'Ahri', 'Garen', 'Zed', 'Jinx' ]

İlki iki dizi içeren bir dizi (iç içe köşeli parantezlere bak), ikincisi dört isimden oluşan düz bir dizi.

Fonksiyon çağrısında yayma

Math.max (Ders 9) sayıları virgülle ayrılmış hâlde ister; ona dizi verirsen anlamaz:

ders12.js
const hasarlar = [120, 340, 90]console.log(Math.max(hasarlar))console.log(Math.max(...hasarlar))
Çıktı
NaN340

İlk satırda Math.max tek bir şey aldı: bir dizi. Onu sayıya çeviremedi ve NaN verdi. Yayma diziyi açıp Math.max(120, 340, 90) hâline getirdi. Boş bir diziyi yayarsan Math.max hiç sayı almaz ve -Infinity döndürür; liste boş olabilecekse önce length'e bak.

Objelerde yayma

Üç nokta objelerde de çalışır:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }const guncel = { ...ahri, hp: 80 }console.log(ahri)console.log(guncel)
Çıktı
{ isim: 'Ahri', hp: 100, gold: 300 }{ isim: 'Ahri', hp: 80, gold: 300 }

{ ...ahri, hp: 80 }: yeni bir obje kur, içine önce ahri'nin bütün alanlarını dök, sonra hp'yi 80 yap. Aynı anahtar iki kez gelince sonraki kazanır: önce hp: 100 geldi, sonra hp: 80 onun üstüne yazıldı. Asıl ahri yine değişmedi.

İki objeyi de aynı yolla birleştirirsin. Oyun profilini düşün: her yeni hesap varsayılan bir unvan, çerçeve ve simgeyle başlar; oyuncu bunlardan istediğini değiştirir. Varsayılanları önce, oyuncunun seçtiklerini sonra dökersin:

ders12.js
const varsayilanProfil = { unvan: "Çaylak", cerceve: "gri", simge: 1 }const secilenler = { unvan: "Efsane", simge: 29 }const profil = { ...varsayilanProfil, ...secilenler }console.log(profil)
Çıktı
{ unvan: 'Efsane', cerceve: 'gri', simge: 29 }
{ ...varsayilanProfil, ...secilenler } 1. önce dökülür varsayilanProfil unvan: "Çaylak" cerceve: "gri" simge: 1 2. sonra dökülür secilenler unvan: "Efsane" simge: 29 sonuç profil unvan: "Efsane" cerceve: "gri" simge: 29 Üstü çizili değerler, sonradan gelen aynı adlı alanlarca ezildi.
Şekil 12.2 Önce varsayılanlar dökülür, sonra oyuncunun seçtikleri. Aynı anahtar gelince sonraki kazanır.

Geri kalanı toplamak: rest

Aynı üç nokta ters yönde de çalışır: dökmek yerine toplar. Hangisi olduğunu, üç noktanın nerede durduğundan anlarsın.

Kaç vuruş geleceğini bilmediğin bir toplam hasar fonksiyonu düşün: bazen iki vuruş gelir, bazen dört, bazen hiç.

ders12.js
function toplamHasar(...vuruslar) {    return vuruslar.reduce((toplam, vurus) => toplam + vurus, 0)} console.log(toplamHasar(120, 80))console.log(toplamHasar(50, 50, 50, 50))console.log(toplamHasar())
Çıktı
2002000

...vuruslar "bana gelen bütün argümanları bir diziye topla" demek. İçeride vuruslar gerçek bir dizidir, Ders 11'deki reduce doğrudan çalışır. Hiç argüman gelmezse boş dizi olur; reduce'un başlangıç değeri 0 sayesinde sonuç da 0 çıkar.

Normal parametrelerle birlikte de kullanılır. Önce tek tek parametreler gelir, en sona geri kalan:

ders12.js
function saldiri(saldiran, ...hedefler) {    console.log(saldiran, "vurdu:", hedefler)} saldiri("Lux", "Zed", "Jinx", "Teemo")saldiri("Ahri")
Çıktı
Lux vurdu: [ 'Zed', 'Jinx', 'Teemo' ]Ahri vurdu: []

İlk argüman saldiran'a gider, kalanların hepsi hedefler dizisine. Tek argümanla çağrılınca hedefler boş bir dizidir, undefined değil.

Sık hataSyntaxError: Rest parameter must be last formal parameter

Ne zaman çıkar? Geri kalanı toplayan parametreden sonra başka bir parametre yazınca.

ders12.js
function saldiri(...hedefler, saldiran) {    console.log(saldiran, hedefler)}
Hata çıktısı
C:\kod\ogren\ders12.js:1function saldiri(...hedefler, saldiran) {                            ^ SyntaxError: Rest parameter must be last formal parameter    at wrapSafe (node:internal/modules/cjs/loader:1861:18)    ... Node.js v24.20.0

Nasıl düzelir? Mesaj "geri kalan parametresi en sonda olmalı" diyor. Bütün argümanları toplayan bir parametreden sonra alacak argüman kalmaz; onu en sona taşı: function saldiri(saldiran, ...hedefler). Parçalamada da kural aynı: const [...digerleri, son] = takim yazarsan SyntaxError: Rest element must be last element alırsın.

Parçalarken de geri kalanı toplayabilirsin. Dizide alınmayan elemanlar bir diziye, objede alınmayan alanlar bir objeye gider:

ders12.js
const takim = ["Garen", "Ahri", "Lux", "Jinx"]const [lider, ...digerleri] = takimconsole.log(lider)console.log(digerleri)
Çıktı
Garen[ 'Ahri', 'Lux', 'Jinx' ]
ders12.js
const hesap = { kullaniciAdi: "ahri_main", sifre: "gizli123", seviye: 214 }const { sifre, ...herkeseAcik } = hesapconsole.log(herkeseAcik)console.log(hesap)
Çıktı
{ kullaniciAdi: 'ahri_main', seviye: 214 }{ kullaniciAdi: 'ahri_main', sifre: 'gizli123', seviye: 214 }

Bu, gerçek projelerde çok kullanılan bir kalıp: bir kullanıcıyı ekrana basmadan ya da başkasına göndermeden önce şifre alanını ayırmak. Asıl obje bozulmaz. (Gerçek sistemler şifreyi zaten açık metin olarak saklamaz; bunu Ders 31'de göreceksin.)

Yayma (spread)Geri kalan (rest)
Ne yapar?Açar, dökerToplar
Nerede durur?Yeni dizi ya da obje kurarken, fonksiyonu çağırırkenParçalarken (eşittirin solunda), fonksiyonu tanımlarken
Örnek[...takim, "Lux"], Math.max(...hasarlar)const [lider, ...digerleri], function toplamHasar(...vuruslar)

Objenin bütün alanlarını gezmek

Bir dizinin her elemanı için for...of yazabiliyordun. Aynısını bir objede dene:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }for (const alan of ahri) {    console.log(alan)}
Hata çıktısı
C:\kod\ogren\ders12.js:2for (const alan of ahri) {                   ^ TypeError: ahri is not iterable    at Object.<anonymous> (C:\kod\ogren\ders12.js:2:20)    ... Node.js v24.20.0

Iterable "üzerinde gezilebilir" demek. Dizi gezilebilir, obje gezilemez: objede sıra numarası yok, anahtarlar var. Çözüm, objeyi önce bir diziye çevirmek. JavaScript'in bunun için üç hazır fonksiyonu var:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }console.log(Object.keys(ahri))console.log(Object.values(ahri))console.log(Object.entries(ahri))
Çıktı
[ 'isim', 'hp', 'gold' ][ 'Ahri', 100, 300 ][ [ 'isim', 'Ahri' ], [ 'hp', 100 ], [ 'gold', 300 ] ]
{ isim: "Ahri", hp: 100, gold: 300 } Object.keys anahtarlar Object.values değerler Object.entries [anahtar, değer] çiftleri 'isim' 'hp' 'gold' 'Ahri' 100 300 [ 'isim', 'Ahri' ] [ 'hp', 100 ] [ 'gold', 300 ]
Şekil 12.3 Aynı objeden üç farklı dizi. entries her alanı iki elemanlı bir diziye çevirir.
  • Object.keys(obje): anahtarlar (İngilizce key, anahtar).
  • Object.values(obje): değerler (value).
  • Object.entries(obje): [anahtar, değer] çiftleri (entry, kayıt). En çok bu kullanılır, çünkü ikisini birden verir.

Üçü de dizi döndürdüğü için artık for...of çalışır. Her turda gelen çifti dizi parçalamayla hemen iki kutuya açarsın:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }for (const [anahtar, deger] of Object.entries(ahri)) {    console.log(`${anahtar}: ${deger}`)}
Çıktı
isim: Ahrihp: 100gold: 300

Bu satırı ezberle: for (const [anahtar, deger] of Object.entries(obje)). Bir objenin her alanıyla iş yapmanın en yaygın yolu bu.

Dönen şey dizi olduğu için Ders 11'deki metotların hepsi de kullanılabilir. Dükkândaki fiyatlarla dene:

ders12.js
const fiyatlar = { kilic: 450, kalkan: 300, iksir: 50 }const toplam = Object.values(fiyatlar).reduce((t, fiyat) => t + fiyat, 0)const ucuzlar = Object.entries(fiyatlar).filter(([esya, fiyat]) => fiyat < 400)console.log(Object.keys(fiyatlar).length)console.log(toplam)console.log(ucuzlar)
Çıktı
3800[ [ 'kalkan', 300 ], [ 'iksir', 50 ] ]
  • Object.keys(fiyatlar).length objede kaç alan olduğunu söyler. Boş bir obje için 0 verir; "bu obje boş mu?" sorusunun cevabı budur.
  • filter'a verdiğin fonksiyon her turda bir [anahtar, değer] çifti alıyor. Parantezin içinde onu hemen parçalayabilirsin: ([esya, fiyat]) =>. Parçalama her parametrede çalışır.

Kopya mı, aynı obje mi?

Ders 4'te bir objeyi fonksiyona verince fonksiyonun dışarıdaki objeyi değiştirebildiğini gördün; sayı verince ise yalnız kopyası gidiyordu. Bu farkın sebebini şimdi öğreneceksin. Önce bildiğin bir şey: bir sayıyı başka bir kutuya kopyalayıp kopyayı değiştirirsen asıl kutu etkilenmez.

ders12.js
let can = 100let yedekCan = canyedekCan = 0console.log(can, yedekCan)
Çıktı
100 0

Şimdi aynısını bir objeyle yap:

ders12.js — ogren — Visual Studio Code
ders12.js
const ahri = { isim: "Ahri", hp: 100 }const kopya = ahrikopya.hp = 0console.log(ahri.hp)console.log(kopya === ahri)
PROBLEMSOUTPUTDEBUG CONSOLETERMINAL
PS C:\kod\ogren> node ders12.js0true
Ln 1, Col 1Spaces: 4UTF-8JavaScript

kopya'nın canını sıfırladın, ahri'ninki de sıfırlandı. Hata yok, uyarı yok.

Sebebi şu: obje bir değişkenin içine sığmaz. Değişken, objenin bellekte nerede durduğunu tutar; buna referans denir. const kopya = ahri objeyi kopyalamadı, adresini kopyaladı.

Artık aynı objeye giden iki ad var ve birinden yapılan değişikliği öteki de görür. Sayı, metin, true ve false gibi tek parça değerler ise kutunun içinde durur ve atamada kopyalanır.

Sayı: kopyalanır let yedekCan = can yedekCan = 0 can 100 yedekCan 0 iki ayrı kutu: biri değişince öteki etkilenmez Obje: paylaşılır const kopya = ahri kopya.hp = 0 ahri kopya isim: "Ahri" hp: 0 iki ad, tek obje
Şekil 12.4 Sayı atamada kopyalanır. Obje atamada kopyalanmaz; aynı objeye giden ikinci bir ad açılır.

Bu yüzden === objelerde içeriğe değil adrese bakar:

ders12.js
const ahri = { hp: 100 }const kopya = ahriconsole.log(kopya === ahri)console.log({ hp: 100 } === { hp: 100 })console.log([] === [])
Çıktı
truefalsefalse

Aynı objeye giden iki ad eşittir. İçerikleri aynı olsa da ayrı ayrı kurulmuş iki obje eşit değildir, çünkü her { } ve her [ ] yeni bir obje kurar.

Ders 4'teki fonksiyon davranışının sebebi de bu. Fonksiyona objenin kopyası değil referansı gider; parametre, aynı objeye giden bir ad daha olur. sampiyon.hp = 0 yazan bir fonksiyon dışarıdaki objeyi değiştirir.

Bu, Ders 10'da gördüğün yan etki; program büyüdükçe "bu değeri kim değiştirdi?" sorusu zorlaşır. Ders 11'deki değişmezlik alışkanlığı burada da geçerli: asıl objeyi değiştirme, yaymayla yenisini kurup döndür:

ders12.js
function hasarVer(sampiyon, miktar) {    return { ...sampiyon, hp: sampiyon.hp - miktar }} const ahri = { isim: "Ahri", hp: 100 }const yarali = hasarVer(ahri, 30)console.log(ahri)console.log(yarali)
Çıktı
{ isim: 'Ahri', hp: 100 }{ isim: 'Ahri', hp: 70 }

Ders 11'de sort'un asıl diziyi değiştirdiğini, toSorted'ın ise yeni dizi döndürdüğünü görmüştün. Farkın önemi buradan geliyor: sort dizinin kendisine dokunur ve o diziyi gösteren her ad değişikliği görür. Eski kodlardaki [...dizi].sort() yazımı da bu yüzden önce yaymayla bir kopya alır.

Sığ kopya tuzağı ve structuredClone

Yayma yeni bir obje kurar, ama yalnız en dış katı yenidir. Objenin içinde bir dizi ya da başka bir obje varsa ne olduğuna bak:

ders12.js
const ahri = { isim: "Ahri", hp: 100, esyalar: ["Doran Yüzüğü"] }const kopya = { ...ahri }kopya.hp = 50kopya.esyalar.push("Kılıç")console.log(ahri)console.log(kopya)
Çıktı
{ isim: 'Ahri', hp: 100, esyalar: [ 'Doran Yüzüğü', 'Kılıç' ] }{ isim: 'Ahri', hp: 50, esyalar: [ 'Doran Yüzüğü', 'Kılıç' ] }

hp güvende: Ahri'nin canı hâlâ 100. Ama kılıç iki objeye birden girdi.

Sebep: esyalar alanında dizinin kendisi değil, referansı durur. Yayma bu alanı kopyalarken diziyi değil referansı kopyaladı; iki obje aynı eşya dizisini paylaşıyor. Ders 11'de map'in içinde elemanı değiştirince asıl dizideki objenin de değişmesi aynı sebepten: yeni dizi, eski objeler.

Sığ kopya { ...ahri } iki obje, tek eşya dizisi ahri hp: 100 esyalar: kopya hp: 50 esyalar: 'Doran Yüzüğü' 'Kılıç' Derin kopya structuredClone(ahri) her objenin kendi dizisi ahri hp: 100 esyalar: kopya hp: 100 esyalar: 'Doran Yüzüğü' 'Doran Yüzüğü' 'Kılıç'
Şekil 12.5 Üstte sığ kopya: iki ayrı obje, tek bir eşya dizisi. Altta derin kopya: her objenin kendi dizisi var.

Böyle, yalnız dış katı yeni olan kopyaya sığ kopya denir. { ...obje } ve [...dizi] sığ kopya üretir. İç içe bütün katları yeni olan kopya ise derin kopyadır ve JavaScript'te tek komutla yapılır: structuredClone.

ders12.js
const ahri = { isim: "Ahri", hp: 100, esyalar: ["Doran Yüzüğü"] }const kopya = structuredClone(ahri)kopya.esyalar.push("Kılıç")console.log(ahri)console.log(kopya)
Çıktı
{ isim: 'Ahri', hp: 100, esyalar: [ 'Doran Yüzüğü' ] }{ isim: 'Ahri', hp: 100, esyalar: [ 'Doran Yüzüğü', 'Kılıç' ] }

structuredClone objeyi bütün katlarıyla, içindeki dizileri ve objeleri de yeniden kurarak kopyalar. Node'da 17. sürümden beri, bütün yaygın tarayıcılarda 2022'den beri hazır gelir; bir şey kurman gerekmez.

YazımYeni obje kurulur mu?İçteki diziler ve objeler
const kopya = asilHayır, aynı objePaylaşılır
{ ...asil } ya da [...asil]Evet, sığ kopyaPaylaşılır
structuredClone(asil)Evet, derin kopyaOnlar da yeniden kurulur
Sık hataKopyayı değiştirdim, asıl obje de değişti

Ne zaman çıkar? const kopya = asil yazınca ya da iç içe veride { ...asil } ile kopya alıp kopyanın içteki dizisini veya objesini değiştirince. Hiçbir mesaj çıkmaz; program çalışır ama yanlış çalışır. Belirtisi: hiç dokunmadığını düşündüğün bir değer değişmiş. Bir TFT oyununda yeni oyuncuları bir şablondan üretiyorsun:

ders12.js
const sablon = { ad: "Yeni oyuncu", altin: 0, birimler: [] }const oyuncu1 = { ...sablon, ad: "Deniz" }const oyuncu2 = { ...sablon, ad: "Ece" }oyuncu1.birimler.push("Garen")console.log(oyuncu2.birimler)console.log(sablon.birimler)console.log(oyuncu1.birimler === oyuncu2.birimler)
Çıktı
[ 'Garen' ][ 'Garen' ]true

Deniz bir Garen aldı; Ece'nin tahtasında da, şablonda da Garen belirdi. Son satır sebebi kanıtlıyor: üçü de aynı birimler dizisini gösteriyor.

Nasıl düzelir? İç içe veriyi kopyalarken structuredClone kullan: { ...structuredClone(sablon), ad: "Deniz" }. Bir değerin beklenmedik biçimde değiştiğini görürsen === ile iki adın aynı objeyi gösterip göstermediğini denetle; true çıkıyorsa aradığın hata budur.

JSON: objeyi metne, metni objeye çevirmek

Objeler programın belleğinde yaşar; program kapanınca kaybolurlar. Veriyi dosyaya yazmak, bir sitenin sunucudan veri alması ya da iki programın konuşması için verinin metne dönüşmesi gerekir. Bunun bütün dünyada kullanılan ortak biçimi JSON'dur.

JSON.stringify objeyi JSON metnine çevirir:

ders12.js
const ahri = { isim: "Ahri", hp: 100, gold: 300 }const metin = JSON.stringify(ahri)console.log(metin)console.log(metin.length)
Çıktı
{"isim":"Ahri","hp":100,"gold":300}35

metin artık bir obje değil, 35 karakterlik bir metin; length bunu kanıtlıyor. Anahtarların da çift tırnağa alındığına bak, JSON'da bu zorunlu. Okunaklı olsun diye ikinci ve üçüncü argümanla girintili de yazdırabilirsin:

ders12.js
const ahri = { isim: "Ahri", hp: 100, esyalar: ["Doran Yüzüğü", "Kılıç"] }console.log(JSON.stringify(ahri, null, 2))
Çıktı
{  "isim": "Ahri",  "hp": 100,  "esyalar": [    "Doran Yüzüğü",    "Kılıç"  ]}

Üçüncü argüman (2) her katta kaç boşluk içeri girileceğini söyler. Ortadaki null "boş" anlamına gelen bir değer; onu bu dersin ilerisinde tanıyacaksın. O yer, hangi alanların yazılacağını seçmek gibi ileri bir iş için ayrılmış; şimdilik kalıp olarak null yaz.

Ters yön JSON.parse: JSON metninden yeniden obje kurar.

ders12.js
const kayit = '{"isim":"Lux","seviye":6,"esyalar":["Asa","Kitap"]}'const lux = JSON.parse(kayit)console.log(lux)console.log(lux.esyalar[1])
Çıktı
{ isim: 'Lux', seviye: 6, esyalar: [ 'Asa', 'Kitap' ] }Kitap

Metnin içinde çift tırnaklar olduğu için onu tek tırnakla açıp kapattın; Ders 1'deki kuralı hatırla: hangi tırnakla açtıysan onunla kaparsın. Gerçek programlarda bu metni sen yazmayacaksın; internetten (Ders 15) ya da bir dosyadan (Ders 29) gelecek.

programın belleğinde obje isim: "Ahri" hp: 100 metin (JSON) {"isim":"Ahri","hp":100} JSON.stringify JSON.parse dosyaya yazılır, ağda gezer Yol boyunca fonksiyonlar ve undefined alanlar düşer.
Şekil 12.6 JSON, objeyi taşınabilir bir metne çevirir. stringify paketler, parse paketi açıp objeyi yeniden kurar.

JSON gördüğün her yerde ilk soru şu olmalı: "Bu şu an metin mi, obje mi?" parse etmeyi unutursan hata çıkmaz ama sonuç saçmalar:

ders12.js
const kayit = '{"isim":"Lux","seviye":6}'console.log(kayit.isim)console.log(kayit.seviye + 1)
Çıktı
undefinedNaN

kayit hâlâ bir metin. Metnin isim diye bir alanı yok, undefined geldi; undefined + 1 de NaN verdi. Doğrusu önce JSON.parse(kayit).

JSON yalnız veriyi taşır: metin, sayı, true/false, null, dizi ve obje. Fonksiyonlar ve değeri undefined olan alanlar yolda sessizce düşer:

ders12.js
const teemo = { isim: "Teemo", selam: () => "Hey!", klan: undefined, hedef: null }console.log(JSON.stringify(teemo))
Çıktı
{"isim":"Teemo","hedef":null}

selam ve klan metne girmedi, hedef girdi. null "bilerek boş" demek; birazdan ayrıntılı göreceksin. Bir bilginin boş olduğunu JSON'a yazmak istiyorsan undefined değil null kullan.

Sık hataSyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)

Ne zaman çıkar? JSON.parse'a JSON kurallarına uymayan bir metin verince. En sık sebepler: anahtarlarda tırnak yok, çift tırnak yerine tek tırnak kullanılmış, sonda fazladan virgül var.

ders12.js
const kayit = '{ isim: "Ahri", hp: 100 }'const ahri = JSON.parse(kayit)
Hata çıktısı
<anonymous_script>:1{ isim: "Ahri", hp: 100 }  ^ SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)    at JSON.parse (<anonymous>)    at Object.<anonymous> (C:\kod\ogren\ders12.js:2:19)    ... Node.js v24.20.0

Bu mesajın ilk satırında senin dosyan yok. <anonymous_script>:1 "ayrıştırılan metnin 1. satırı" demek; altındaki satır da senin kodun değil, JSON metninin kendisi.

^ metnin 3. karakterini (i) gösteriyor: JSON orada çift tırnaklı bir anahtar adı bekliyordu. Mesajdaki position 2 aynı yerin sıfırdan sayılmış hâli. JSON.parse'ı hangi satırda çağırdığını at Object.<anonymous> (…ders12.js:2:19) satırı söylüyor.

Nasıl düzelir? Metni JSON kurallarına uydur: '{ "isim": "Ahri", "hp": 100 }'. Aynı ailenin başka mesajları:

  • Expected double-quoted property name in JSON at position 29 (line 1 column 30): sonda fazladan virgül var ("hp": 100, }).
  • Unexpected token 'A', "Ahri" is not valid JSON: metin JSON değil, düz yazı.
  • "undefined" is not valid JSON: JSON.parse'a değeri undefined olan bir değişken verdin.
  • Unexpected end of JSON input: metin boş ya da bir değerin gelmesi gereken yerde bitmiş ('{"seviye":' gibi yarım kalmış bir kayıt). Yarım metin, kesildiği yere göre Unterminated string in JSON gibi başka bir mesaj da verebilir.

Dışarıdan gelen JSON bozuk olabilir ve programını çökertir. Bu hatayı programı durdurmadan yakalamayı Ders 13'te try ve catch ile öğreneceksin.

null ve undefined: iki çeşit "yok"

undefined'ı Ders 3 ve Ders 4'ten tanıyorsun: JavaScript bir yere hiç değer konmadığında onu kendisi koyar.

ders12.js
let hedefconst ahri = { isim: "Ahri", hp: 100 }const takim = ["Ahri", "Garen"]console.log(hedef)console.log(ahri.mana)console.log(takim[5])console.log(takim.find((isim) => isim === "Zed"))
Çıktı
undefinedundefinedundefinedundefined

Değer verilmemiş let, olmayan alan, dizinin dışı, bulamayan find: hepsi undefined. null ise farklıdır. JavaScript onu eksik bir yere kendiliğinden koymaz; bir programcı bilerek yazar (çoğu zaman sen, bazen de kullandığın hazır bir fonksiyonun yazarı) ve "burası bilerek boş" demektir:

ders12.js
let hedef = nullconst oyuncu = { isim: "Teemo", klan: null }console.log(hedef)console.log(oyuncu.klan)console.log(oyuncu.mana)
Çıktı
nullnullundefined

oyuncu.klan null: Teemo'nun klan bilgisi tutuluyor ve cevabı "klanı yok". oyuncu.mana undefined: böyle bir bilgi hiç tutulmamış. İkisi de "yok" der ama farklı yoklardır; null === undefined sonucu da false'tur.

DeğerKim koyar?Nerede görürsün?
undefinedJavaScriptdeğer verilmemiş let, olmayan alan, dizinin dışı, bulamayan find, return'süz fonksiyon (Ders 6), gelmeyen parametre
nullProgramcı (çoğu zaman sen)bilerek boş bırakılan yer: seçilmemiş hedef, klansız oyuncu, JSON'daki boş bilgi

Bir farkı daha bil: varsayılan değerler (parametredeki de, parçalamadaki de) yalnız undefined gelince devreye girer. null "bilerek boş" olduğu için olduğu gibi kabul edilir:

ders12.js
function kartYaz(isim = "Bilinmeyen") {    console.log(isim)} kartYaz(undefined)kartYaz(null)
Çıktı
Bilinmeyennull

Parçalama bölümündeki = {} varsayılanı da bu yüzden yalnız undefined'a karşı korur. function kartYaz({ isim = "Bilinmeyen", hp = 0 } = {}) fonksiyonunu kartYaz(null) diye çağırırsan varsayılan devreye girmez ve program TypeError: Cannot read properties of null (reading 'isim') ile durur.

Doğru sayılanlar ve yanlış sayılanlar

Ders 2'de if'in parantezine hep true ya da false veren bir karşılaştırma yazdın. Ama if (hp = 0) hatasında JavaScript'in koşuldaki 0'ı false gibi saydığını da gördün. Parantez her değeri kabul eder ve onu kendi içinde true ya da false'a çevirir. Hangi değerin neye çevrildiğini Boolean(…) ile görebilirsin; Ders 9'daki Number(…) ve String(…) gibi çalışır:

ders12.js
console.log(Boolean(0), Boolean(""), Boolean(null))console.log(Boolean(undefined), Boolean(NaN), Boolean(false))console.log(Boolean("0"), Boolean("false"), Boolean(" "))console.log(Boolean([]), Boolean({}), Boolean(-5))
Çıktı
false false falsefalse false falsetrue true truetrue true true

Kural kısa. Yanlış sayılan altı değer var: false, 0, "" (boş metin), null, undefined ve NaN. Geri kalan her şey doğru sayılır: "0" ve "false" metinleri, tek boşluklu " ", boş dizi [], boş obje {}, hatta fonksiyonlar da. Ders 10'daki if (hp => 30) tuzağında koşulun hep doğru çıkması bu yüzdendi.

Eksi sıfır -0 ve bu kursta karşına çıkmayacak iki nadir değer daha yanlış sayılır; günlük kodda yukarıdaki altısını bilmen yeter.

Yanlış sayılan: bu 6 değer false 0 "" null undefined NaN Doğru sayılan: geri kalan her şey true 1 -5 "Ahri" "0" "false" " " [] {} koşulda false gibi davranırlar sarılar da doğru sayılır
Şekil 12.7 Akılda tutman gereken altı yanlış sayılan değer. Geri kalan her şey doğru sayılır; sarı olanlar en çok şaşırtanlar.

Bunun işe yarayan bir yanı var: if (isim) yazmak "isim boş değilse" demektir.

ders12.js
const isim = ""if (isim) {    console.log(`Hoş geldin, ${isim}`)} else {    console.log("Önce bir isim yaz")}
Çıktı
Önce bir isim yaz

Bir de tuzağı var. Boş dizi doğru sayıldığı için if (takim) takım boşken de çalışır; "takımda biri var mı?" diye soracaksan takim.length > 0 yaz. Daha sinsi olanı sıfır:

ders12.js
function altinYaz(gold) {    if (gold) {        console.log(`Altının: ${gold}`)    } else {        console.log("Altın bilgisi yok")    }} altinYaz(300)altinYaz(0)altinYaz(undefined)
Çıktı
Altının: 300Altın bilgisi yokAltın bilgisi yok

Sıfır altın da bir bilgidir: "hiç altının yok". Ama 0 yanlış sayıldığı için program onu "bilgi yok" sandı. Çözüm, sorduğun soruyu açıkça yazmak:

ders12.js
function altinYaz(gold) {    if (gold === undefined) {        console.log("Altın bilgisi yok")    } else {        console.log(`Altının: ${gold}`)    }} altinYaz(300)altinYaz(0)altinYaz(undefined)
Çıktı
Altının: 300Altının: 0Altın bilgisi yok

Güvenli okuma: ?.

JavaScript'te en sık göreceğin hatayı Ders 4'ten tanıyorsun:

ders12.js
const oyuncu = { isim: "Teemo", seviye: 30 }console.log(oyuncu.klan)console.log(oyuncu.klan.ad)
Hata çıktısı
undefinedC:\kod\ogren\ders12.js:3console.log(oyuncu.klan.ad)                        ^ TypeError: Cannot read properties of undefined (reading 'ad')    at Object.<anonymous> (C:\kod\ogren\ders12.js:3:25)    ... Node.js v24.20.0

(reading 'ad') okunmak istenen alanı söylüyor; sorun her zaman onun solunda. Solunda oyuncu.klan var ve ikinci satırın çıktısı onun undefined olduğunu gösteriyor. undefined'ın içi yoktur, içine bakılamaz.

Ders 4'teki çözüm, ara basamağı önce if (oyuncu.klan === undefined) ile denetlemekti; Ders 11'de find'ın sonucunu da böyle denetledin. İkisinde de bunun daha kısa bir yazımı olduğunu duymuştun.

oyuncu.klan.ad oyuncu { isim: "Teemo" } .klan oyuncu.klan undefined .ad TypeError program çöker oyuncu.klan?.ad oyuncu { isim: "Teemo" } .klan oyuncu.klan undefined ?.ad durur, sonuç: undefined
Şekil 12.8 Zincir undefined'a çarpınca normal nokta programı çökertir, ?. ise zinciri durdurur.

O kısa yazım ?. işareti. Anlamı: "solundaki şey undefined ya da null ise dur ve sonuç undefined olsun; değilse normal nokta gibi devam et."

ders12.js
const oyuncu = { isim: "Teemo", seviye: 30 }const lux = { isim: "Lux", klan: { ad: "Işık Muhafızları", uye: 12 } }console.log(oyuncu.klan?.ad)console.log(lux.klan?.ad)
Çıktı
undefinedIşık Muhafızları

Teemo'nun klanı yok, program çökmedi; Lux'unki var, normal okundu. Bu işareti zincirde birden çok yerde kullanabilirsin: oyuncu.klan?.lider?.isim. find ile çok iyi anlaşır, çünkü find bulamayınca undefined döndürür:

ders12.js
const takim = [    { isim: "Ahri", hp: 100 },    { isim: "Garen", hp: 620 }]console.log(takim.find((s) => s.isim === "Zed")?.hp)console.log(takim.find((s) => s.isim === "Garen")?.hp)
Çıktı
undefined620
Sık hataTypeError: Cannot read properties of undefined (reading 'hp')

Ne zaman çıkar? undefined olan bir şeyin alanını okumaya çalışınca. Sebep çoğu zaman bir önceki adımdadır: find aradığını bulamadı (Ders 11'de gördüğün durum), objede o alan yok, dizinin olmayan bir sırası istendi ya da bir fonksiyon return etmeyi unuttu.

ders12.js
const takim = [    { isim: "Ahri", hp: 100 },    { isim: "Garen", hp: 620 }]const zed = takim.find((s) => s.isim === "Zed")console.log(zed.hp)
Hata çıktısı
C:\kod\ogren\ders12.js:6console.log(zed.hp)                ^ TypeError: Cannot read properties of undefined (reading 'hp')    at Object.<anonymous> (C:\kod\ogren\ders12.js:6:17)    ... Node.js v24.20.0

Nasıl düzelir? Parantezdeki ad ('hp') okunmak istenen alan; asıl soru solundakinin neden undefined olduğu. console.log(zed) ile bak ve oradan geriye doğru git: find "Zed"i bulamamış.

Sonra karar ver. Bulunamaması normal bir durumsa zed?.hp yaz ve sonucu ?? ile ele al (birazdan). Normal değilse veriyi ya da aradığın adı düzelt.

Yedek değer: ??

?. çökmeyi önler ama elinde undefined kalır. Ekrana "undefined" yazdırmak istemezsin; yerine anlamlı bir yedek değer koymak istersin. Bunu ?? yapar:

ders12.js
const oyuncu = { isim: "Teemo", seviye: 30 }const lux = { isim: "Lux", klan: { ad: "Işık Muhafızları" } }console.log(oyuncu.klan?.ad ?? "Klansız")console.log(lux.klan?.ad ?? "Klansız")
Çıktı
KlansızIşık Muhafızları

?? işaretini şöyle oku: "soldaki değer null ya da undefined ise sağdakini kullan, değilse soldakini." ?. ile ?? çoğu zaman birlikte gelir: biri çökmeyi önler, öteki boşluğa anlamlı bir değer koyar.

Eski kodlarda aynı iş için || (Ders 3'teki "veya") kullanıldığını görürsün. || aslında yalnız true ya da false vermez; iki yanındaki değerlerden birini olduğu gibi verir: soldaki doğru sayılıyorsa soldakini, değilse sağdakini. ?? ile farkı buradan çıkar: || solundaki yanlış sayılan her değerde sağdakine geçer ve bunların arasında 0 ile "" de var:

ders12.js
console.log(0 || 100)console.log(0 ?? 100)console.log("" || "İsimsiz")console.log("" ?? "İsimsiz")console.log(null || "Yok")console.log(null ?? "Yok")
Çıktı
1000İsimsiz YokYok

Dördüncü satırın çıktısı boş bir satır: "" korundu.

İki işaretin farkı tablo hâlinde:

Soldaki değersoldaki || "yedek"soldaki ?? "yedek"
0"yedek"0
"""yedek"""
false"yedek"false
null"yedek""yedek"
undefined"yedek""yedek"
"Ahri""Ahri""Ahri"
Sık hata|| sıfırı silip yedek değeri koyuyor

Ne zaman çıkar? Yedek değeri || ile verince, 0, "" ya da false gerçek ve geçerli bir değerken. Hata mesajı çıkmaz; yalnız sonuç yanlıştır.

ders12.js
const garen = { isim: "Garen", hp: 0 }const can = garen.hp || 620console.log(`${garen.isim} canı: ${can}`)
Çıktı
Garen canı: 620

Can bilgisi gelmezse Garen'in başlangıç canını (620) kullanmak istedin. Ama bilgi gelmişti: Garen ölmüştü ve canı 0'dı. Program ölü bir şampiyonu tam canla diriltti.

Nasıl düzelir? "Yoksa şunu kullan" demek istiyorsan ?? yaz: garen.hp ?? 620 sonucu 0 olur, hp alanı hiç yoksa 620. || işaretini yalnız "boş metin ve sıfır da geçersiz" demek istediğin yerde, bilerek kullan.

Sık hatalar

Bu dersin hatalarının çoğunu bölümlerin içinde gördün. Burada kalanlar var. Bütün derslerin hataları hata rehberinde toplanıyor.

Sık hataTypeError: ahri is not iterable

Ne zaman çıkar? Bir objeyi for...of ile gezmeye ya da diziymiş gibi yaymaya ([...ahri]) kalkınca. Yukarıda, objenin bütün alanlarını gezmek bölümünde gördün. Dizi parçalamada da çıkar: const [ilk] = oyuncu.

Nasıl düzelir? Objeyi önce diziye çevir: for (const [anahtar, deger] of Object.entries(ahri)). Obje yaymak istiyorsan köşeli değil süslü parantez kullan: { ...ahri }.

Sık hataTypeError: Cannot read properties of null (reading 'ad')

Ne zaman çıkar? null olan bir şeyin alanını okumaya çalışınca. undefined'lı hâlinin kardeşi; fark, boşluğu bu kez birinin bilerek koymuş olması.

ders12.js
const oyuncu = { isim: "Teemo", klan: null }console.log(oyuncu.klan.ad)
Hata çıktısı
C:\kod\ogren\ders12.js:2console.log(oyuncu.klan.ad)                        ^ TypeError: Cannot read properties of null (reading 'ad')    at Object.<anonymous> (C:\kod\ogren\ders12.js:2:25)    ... Node.js v24.20.0

Nasıl düzelir? ?. null'u da durdurur: oyuncu.klan?.ad ?? "Klansız" sonucu Klansız olur. Web sayfalarıyla çalışmaya başlayınca (Blok 3) bu mesajın null'lı hâlini çok göreceksin.

Sık hataJSON metnini parse etmeden kullanmak

Ne zaman çıkar? JSON metnini obje sanıp alanlarını okumaya kalkınca. Hata çıkmaz; alanlar undefined, hesaplar NaN gelir. JSON bölümünde kayit.isim örneğiyle gördün.

Nasıl düzelir? Önce const veri = JSON.parse(metin), sonra veri.isim. Emin değilsen console.log(metin) yaz: çıktıda anahtarlar çift tırnaklıysa ({"isim":"Lux"}) elindeki şey metindir; console.log objeleri { isim: 'Lux' } biçiminde yazar.

Görevler

Her görevi ders12.js dosyasında yap; önceki kodu sil ya da satırların başına // koy. Önce kendin dene, takılırsan ipuçlarını sırayla aç. Çözüme en son bak.

Görev 1

Sunucudan gelen profil

Oyunun sunucusundan oyuncu bilgisi İngilizce anahtarlarla geliyor:

JavaScript
const veri = { summonerName: "Deniz", summonerLevel: 214, region: "TR1" }

Tek bir parçalama satırıyla summonerName'i isim, summonerLevel'i seviye adlı kutulara al. rank alanını da al; yoksa "Derecesiz" olsun. Sonra şunu yazdır:

Metin
Deniz, seviye 214, Derecesiz

Ardından veri'ye rank: "Altın II" ekleyip yeniden çalıştır; son kısım Altın II olmalı.

İpucu 1

Yeni adla almak alan: yeniAd, varsayılan vermek alan = deger biçiminde. Hepsi aynı süslü parantezin içinde, virgülle ayrılır.

İpucu 2

Satırın iskeleti: const { summonerName: isim, … } = veri. Yazdırırken şablon metin kullan: `${isim}, seviye ${seviye}, ${rank}`.

Çözümü göster
ders12.js
const veri = { summonerName: "Deniz", summonerLevel: 214, region: "TR1" }const { summonerName: isim, summonerLevel: seviye, rank = "Derecesiz" } = vericonsole.log(`${isim}, seviye ${seviye}, ${rank}`)
Çıktı
Deniz, seviye 214, Derecesiz

Sınır denemesi: rank: "Altın II" eklenince varsayılan devreye girmez ve Altın II yazar. rank: null yazarsan çıktının sonu null olur, çünkü varsayılan değer yalnız undefined'da çalışır.

Görev 2

Koridor değişimi

kadroYaz adında bir fonksiyon yaz. Kaç oyuncu adı verilirse verilsin hepsini oyuncular adlı tek bir dizide toplasın ve sırayla şunları yapsın:

  1. Kadrodaki kişi sayısını yazdırsın.
  2. Tek satırda ilk üç adı ust, orta, alt kutularına, kalanları yedekler dizisine alsın.
  3. Maç başında orta ve alt koridordakiler yer değiştirmek istiyor: orta ile alt'ı tek satırda takas etsin.
  4. Koridorları ve yedekleri yazdırsın. Yedekleri dizi olarak göstermek için console.log("Yedekler:", yedekler) yaz.
JavaScript
kadroYaz("Garen", "Ahri", "Jinx", "Lux", "Teemo")kadroYaz("Garen", "Ahri", "Jinx")

Beklenen çıktı:

Metin
Kadro: 5 kişiÜst: Garen, orta: Jinx, alt: AhriYedekler: [ 'Lux', 'Teemo' ]Kadro: 3 kişiÜst: Garen, orta: Jinx, alt: AhriYedekler: []
İpucu 1

Kaç argüman geleceğini bilmediğin için parametre geri kalan (rest) olmalı: function kadroYaz(...oyuncular). İçeride oyuncular sıradan bir dizidir; length'i vardır.

İpucu 2

Parçalarken de kalanları üç noktayla toplarsın; üç noktalı ad dizi parçalamanın en sonunda durur. Değerleri sonradan değişecek kutuları hangi anahtar sözcükle açman gerekiyor?

İpucu 3

Takas satırı [ ile başlıyor. Önceki satıra yapışmaması için başına ne koyuyordun?

Çözümü göster
ders12.js
function kadroYaz(...oyuncular) {    console.log(`Kadro: ${oyuncular.length} kişi`)    let [ust, orta, alt, ...yedekler] = oyuncular    ;[orta, alt] = [alt, orta]    console.log(`Üst: ${ust}, orta: ${orta}, alt: ${alt}`)    console.log("Yedekler:", yedekler)} kadroYaz("Garen", "Ahri", "Jinx", "Lux", "Teemo")kadroYaz("Garen", "Ahri", "Jinx")
Çıktı
Kadro: 5 kişiÜst: Garen, orta: Jinx, alt: AhriYedekler: [ 'Lux', 'Teemo' ]Kadro: 3 kişiÜst: Garen, orta: Jinx, alt: AhriYedekler: []

let yerine const yazarsan takas satırı Assignment to constant variable. verir; başındaki ; işaretini unutursan Cannot access 'orta' before initialization.

Sınır denemesi: kadroYaz("Garen", "Ahri") çağır. alt undefined olur, takastan sonra orta: undefined, alt: Ahri yazar ve yedekler boş dizidir. Program çökmez ama eksik oyuncuyu sessizce kabul eder; gerçek bir oyunda önce oyuncular.length'i denetlerdin.

Görev 3

Envanter raporu

envanterYaz adında bir fonksiyon yaz. Envanteri { esyaAdi: adet } biçiminde bir obje olarak alsın, adedi 0'dan büyük olan her eşyayı iksir x3 biçiminde yazsın, en sona toplam eşya sayısını eklesin. Hiç eşya yoksa (boş obje ya da bütün adetler 0) yalnız Envanter boş yazsın.

JavaScript
envanterYaz({ iksir: 3, totem: 2, kilic: 0 })envanterYaz({})envanterYaz({ kalkan: 0 })

Beklenen çıktı:

Metin
iksir x3totem x2Toplam 5 eşyaEnvanter boşEnvanter boş
İpucu 1

Toplam için adetler yeter: Object.values(envanter) sana [3, 2, 0] verir, gerisi Ders 11'deki reduce.

İpucu 2

Eşya adlarına da adetlere de ihtiyacın olan yerde for (const [esya, adet] of Object.entries(envanter)) kullan.

İpucu 3

Önce toplamı hesapla. Toplam 0 ise Envanter boş yazıp return ile fonksiyondan erken çık (Ders 6); değilse eşyaları ve toplamı yaz.

Çözümü göster
ders12.js
function envanterYaz(envanter) {    const toplam = Object.values(envanter).reduce((t, adet) => t + adet, 0)    if (toplam === 0) {        console.log("Envanter boş")        return    }    for (const [esya, adet] of Object.entries(envanter)) {        if (adet > 0) {            console.log(`${esya} x${adet}`)        }    }    console.log(`Toplam ${toplam} eşya`)} envanterYaz({ iksir: 3, totem: 2, kilic: 0 })envanterYaz({})envanterYaz({ kalkan: 0 })
Çıktı
iksir x3totem x2Toplam 5 eşyaEnvanter boşEnvanter boş

Sınır denemeleri çağrıların içinde: kilic: 0 listede görünmüyor, boş obje ve "bütün adetler sıfır" durumu aynı mesajı veriyor. reduce'a başlangıç değeri (0) vermeseydin boş objede TypeError: Reduce of empty array with no initial value alırdın.

Görev 4

Simülasyon kopyası

"Takım 100 hasar yerse ne olur?" diye bir simülasyon yapmak istiyorsun. Aşağıdaki kod takımın kopyasını alıp kopyadaki herkese 100 hasar veriyor. Ama çıktıda iki sorun var:

ders12.js✗ Yanlış
const takim = [    { isim: "Garen", hp: 620 },    { isim: "Lux", hp: 90 }] const deneme = [...takim]for (const sampiyon of deneme) {    sampiyon.hp = sampiyon.hp - 100} console.log(deneme)console.log(takim)
Çıktı
[ { isim: 'Garen', hp: 520 }, { isim: 'Lux', hp: -10 } ][ { isim: 'Garen', hp: 520 }, { isim: 'Lux', hp: -10 } ]

Kodu düzelt: asıl takim hiç değişmesin ve hiçbir şampiyonun canı 0'ın altına inmesin. Beklenen çıktı:

Metin
[ { isim: 'Garen', hp: 520 }, { isim: 'Lux', hp: 0 } ][ { isim: 'Garen', hp: 620 }, { isim: 'Lux', hp: 90 } ]
İpucu 1

[...takim] yeni bir dizi kurdu ama içindeki objeler aynı objeler. Bu kopyanın adı neydi?

İpucu 2

Her katı yeni bir kopya için structuredClone(takim). Canın eksiye düşmemesi için Ders 9'dan Math.max(0, …).

Çözümü göster
ders12.js
const takim = [    { isim: "Garen", hp: 620 },    { isim: "Lux", hp: 90 }] const deneme = structuredClone(takim)for (const sampiyon of deneme) {    sampiyon.hp = Math.max(0, sampiyon.hp - 100)} console.log(deneme)console.log(takim)
Çıktı
[ { isim: 'Garen', hp: 520 }, { isim: 'Lux', hp: 0 } ][ { isim: 'Garen', hp: 620 }, { isim: 'Lux', hp: 90 } ]

İkinci bir yol: objeleri hiç değiştirmeden map ile yenilerini kurmak. Her şampiyon için yayma ile yeni bir obje döndürürsün:

ders12.js
const takim = [    { isim: "Garen", hp: 620 },    { isim: "Lux", hp: 90 }] const deneme = takim.map((sampiyon) => {    return { ...sampiyon, hp: Math.max(0, sampiyon.hp - 100) }}) console.log(deneme)console.log(takim)
Çıktı
[ { isim: 'Garen', hp: 520 }, { isim: 'Lux', hp: 0 } ][ { isim: 'Garen', hp: 620 }, { isim: 'Lux', hp: 90 } ]

Sınır denemesi: canları 100 ve 101 yap; 0 ve 1 çıkmalı.

Görev 5

Maç özeti hata avı

Bu fonksiyon bir maçın özetini yazıyor. Bilgisi eksik maçlarda ? ya da "bilinmiyor" yazması, süre yoksa 30 dakika varsayması gerekiyor. Ama bir maçta yanlış sonuç veriyor:

ders12.js✗ Yanlış
function macOzeti(mac) {    const sure = mac.sure || 30    const oldurme = mac.oldurme || "?"    if (mac.olum) {        console.log(`${mac.sampiyon}: ${oldurme} öldürme, ${mac.olum} ölüm, ${sure} dk`)    } else {        console.log(`${mac.sampiyon}: ${oldurme} öldürme, ölüm bilinmiyor, ${sure} dk`)    }} macOzeti({ sampiyon: "Annie", oldurme: 0, olum: 0, sure: 25 })macOzeti({ sampiyon: "Zed", oldurme: 9, olum: 2 })macOzeti({ sampiyon: "Lux" })
Çıktı
Annie: ? öldürme, ölüm bilinmiyor, 25 dkZed: 9 öldürme, 2 ölüm, 30 dkLux: ? öldürme, ölüm bilinmiyor, 30 dk

Annie'nin maçında hiç öldürme ve ölüm yok, ama bu bilgi biliniyor. Hataları bul ve düzelt. Beklenen çıktı:

Metin
Annie: 0 öldürme, 0 ölüm, 25 dkZed: 9 öldürme, 2 ölüm, 30 dkLux: ? öldürme, ölüm bilinmiyor, 30 dk
İpucu 1

Annie'nin değerlerinden hangileri yanlış sayılan değerler listesinde?

İpucu 2

İki tür hata var: || ile verilen yedek değerler ve if (mac.olum) koşulu. Birincisi için ??, ikincisi için açık bir soru (=== undefined).

Çözümü göster
ders12.js
function macOzeti(mac) {    const sure = mac.sure ?? 30    const oldurme = mac.oldurme ?? "?"    if (mac.olum === undefined) {        console.log(`${mac.sampiyon}: ${oldurme} öldürme, ölüm bilinmiyor, ${sure} dk`)    } else {        console.log(`${mac.sampiyon}: ${oldurme} öldürme, ${mac.olum} ölüm, ${sure} dk`)    }} macOzeti({ sampiyon: "Annie", oldurme: 0, olum: 0, sure: 25 })macOzeti({ sampiyon: "Zed", oldurme: 9, olum: 2 })macOzeti({ sampiyon: "Lux" })
Çıktı
Annie: 0 öldürme, 0 ölüm, 25 dkZed: 9 öldürme, 2 ölüm, 30 dkLux: ? öldürme, ölüm bilinmiyor, 30 dk

Aynı düzeltmeyi parametrede parçalamayla da yapabilirsin, çünkü varsayılan değerler de yalnız undefined'da çalışır: function macOzeti({ sampiyon, oldurme = "?", olum, sure = 30 }). Gövdede mac. önekleri kalkar, koşul olum === undefined olur ve üç çağrının çıktısı aynı kalır.

Görev 6

Kayıt dosyası okuyucu

Oyunun kayıtları JSON metni olarak geliyor; bazılarında alanlar eksik:

JavaScript
const varsayilanAyarlar = { ses: 50, dil: "tr" } const kayitlar = [    '{"oyuncu":"Deniz","ayarlar":{"ses":0,"dil":"en"},"sonMac":null}',    '{"oyuncu":"Ece"}',    '{"oyuncu":"Can","ayarlar":{"ses":35},"sonMac":{"sampiyon":"Lux","sonuc":"zafer"}}']

kayitOku(metin) adında bir fonksiyon yaz. Metni objeye çevirsin ve şu biçimde yeni bir obje döndürsün:

  • oyuncu: kayıttaki ad.
  • ayarlar: varsayilanAyarlar ile kayıttaki ayarların birleşimi; kayıtta olan ayar kazanır.
  • sonSampiyon: son maçta oynanan şampiyon; son maç yoksa ya da null ise "Henüz maç yok".

Sonra her kaydı kayitOku ile oku ve sonucu JSON.stringify ile tek satır yazdır. varsayilanAyarlar hiç değişmemeli. Beklenen çıktı:

Metin
{"oyuncu":"Deniz","ayarlar":{"ses":0,"dil":"en"},"sonSampiyon":"Henüz maç yok"}{"oyuncu":"Ece","ayarlar":{"ses":50,"dil":"tr"},"sonSampiyon":"Henüz maç yok"}{"oyuncu":"Can","ayarlar":{"ses":35,"dil":"tr"},"sonSampiyon":"Lux"}
İpucu 1

Fonksiyonun ilk satırı const kayit = JSON.parse(metin). Sınır değerlerine dikkat: Deniz'in sesi 0 ve öyle kalmalı, Ece'nin hiç ayarlar alanı yok, Deniz'in son maçı null.

İpucu 2

Ayarlar için yayma: önce varsayılanlar, sonra kayıttakiler, çünkü sonraki kazanır. Objeye undefined yaymak hata vermez, hiçbir alan eklemez; bu yüzden Ece'nin kaydında da çalışır.

İpucu 3

Son şampiyon için kayit.sonMac?.sampiyon ?? "Henüz maç yok". Neden . değil de ?., neden || değil de ??, ikisini de cevaplayabiliyor olmalısın.

Çözümü göster
ders12.js
const varsayilanAyarlar = { ses: 50, dil: "tr" } function kayitOku(metin) {    const kayit = JSON.parse(metin)    return {        oyuncu: kayit.oyuncu,        ayarlar: { ...varsayilanAyarlar, ...kayit.ayarlar },        sonSampiyon: kayit.sonMac?.sampiyon ?? "Henüz maç yok"    }} const kayitlar = [    '{"oyuncu":"Deniz","ayarlar":{"ses":0,"dil":"en"},"sonMac":null}',    '{"oyuncu":"Ece"}',    '{"oyuncu":"Can","ayarlar":{"ses":35},"sonMac":{"sampiyon":"Lux","sonuc":"zafer"}}'] for (const metin of kayitlar) {    console.log(JSON.stringify(kayitOku(metin)))}console.log(varsayilanAyarlar)
Çıktı
{"oyuncu":"Deniz","ayarlar":{"ses":0,"dil":"en"},"sonSampiyon":"Henüz maç yok"}{"oyuncu":"Ece","ayarlar":{"ses":50,"dil":"tr"},"sonSampiyon":"Henüz maç yok"}{"oyuncu":"Can","ayarlar":{"ses":35,"dil":"tr"},"sonSampiyon":"Lux"}{ ses: 50, dil: 'tr' }

Son satır varsayilanAyarlar'ın bozulmadığını kanıtlıyor: yayma her seferinde yeni bir obje kurdu. Ayarları kayit.ayarlar.ses || varsayilanAyarlar.ses gibi tek tek yazmaya kalksaydın iki hata alırdın: Deniz'in sesi 50 olurdu, Ece'nin kaydında da Cannot read properties of undefined (reading 'ses') çıkardı.

?. yerine düz nokta yazsaydın program daha ilk kayıtta TypeError: Cannot read properties of null (reading 'sampiyon') ile dururdu, çünkü Deniz'in son maçı null; Ece'nin kaydı da aynı mesajın undefined'lı hâlini verirdi. || yazsaydın burada sonuç değişmezdi, çünkü şampiyon adı hiçbir zaman 0 ya da boş metin olmaz; yine de "yoksa şunu kullan" demenin doğru yolu ??.

Son deneme: listeye bozuk bir kayıt ekle ('{oyuncu:"Zed"}'). Önceki kayıtlar yazılır, sonra SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2) ile program durur. Bozuk kaydı atlayıp devam etmeyi Ders 13'te öğreneceksin.

Kendini sına

Soru 1

Bu program ne yazar?

JavaScript
const lux = { isim: "Lux", mana: 480 }const { mana: m, seviye = 1 } = luxconsole.log(m, seviye)console.log(mana)
Cevabı göster

Önce 480 1 yazar: mana alanı m adlı kutuya alındı, seviye objede olmadığı için varsayılan 1 kullanıldı. Sonra ReferenceError: mana is not defined ile durur. mana: m yazınca mana diye bir kutu açılmaz; yalnız m açılır.

Soru 2

nisanci.menzil ve yanlis.menzil kaçtır?

JavaScript
const temel = { hiz: 340, menzil: 125 }const nisanci = { ...temel, menzil: 550 }const yanlis = { menzil: 550, ...temel }console.log(nisanci.menzil, yanlis.menzil)
Cevabı göster

550 125. Yaymada aynı anahtar iki kez gelince sonraki kazanır. nisanci'da menzil: 550 sonra geldi; yanlis'ta ise temel'in menzil: 125'i sonra geldi ve 550'nin üstüne yazdı.

Soru 3

Bu program ne yazar?

JavaScript
const takim = ["Ahri"]const yedek = takimconst kopya = [...takim]yedek.push("Garen")console.log(takim.length, kopya.length)
Cevabı göster

2 1. yedek yeni bir dizi değil, takim'ın kendisine giden ikinci bir ad; push aynı diziyi büyüttü. kopya ise yayma ile kurulmuş ayrı bir dizi, ona dokunulmadı.

Soru 4

Şu değerlerden hangileri if içinde doğru sayılır? 0, "0", [], null, " ", NaN

Cevabı göster

"0", [] ve " ". Yanlış sayılan altı değer şunlardı: false, 0, "", null, undefined, NaN. "0" sayı değil, içinde bir karakter olan metin; " " boş değil, içinde boşluk olan metin; [] boş da olsa bir dizi.

Soru 5

JSON.stringify ne üretir?

JavaScript
const annie = { isim: "Annie", tibbers: () => "Ayı!", seviye: undefined, klan: null }console.log(JSON.stringify(annie))
Cevabı göster

{"isim":"Annie","klan":null}. Fonksiyon olan tibbers ve değeri undefined olan seviye JSON'a girmez. null geçerli bir JSON değeridir ve kalır. Anahtarların çift tırnaklı yazıldığına da dikkat.

Soru 6

Mesajı oku: hata nerede, sebebi ne?

ders12.js
const takim = [    { isim: "Ahri" },    { isim: "Garen" },    { isim: "Lux" }]const son = takim[3]console.log(son.isim)
Hata çıktısı
C:\kod\ogren\ders12.js:7console.log(son.isim)                ^ TypeError: Cannot read properties of undefined (reading 'isim')
Cevabı göster

Hata 7. satırda, .isim okunurken. .isim'in solundaki son undefined. Sebep bir önceki satırda: üç elemanlı dizinin sıra numaraları 0, 1 ve 2'dir; takim[3] diye bir eleman yok (Ders 5'teki "bir fazla" hatası). Son eleman takim[takim.length - 1].

Soru 7

Bu satır ne yazar ve hangisi doğru?

JavaScript
const kill = 0console.log(kill || "yok", kill ?? "yok")
Cevabı göster

yok 0. || sıfırı yanlış sayıp "yok"a geçti. ?? yalnız null ve undefined'da yedeğe geçer, 0'ı korudu. Sıfır öldürme gerçek bir bilgi olduğu için doğru olan ??.

Kayıt noktası

Bu dersin çalışmasını Git'e kaydet. VS Code terminalinde, C:\kod\ogren klasöründeyken:

Bir sonraki derste hatalarla barışacaksın: bozuk bir JSON'un ya da beklenmedik bir undefined'ın programını çökertmesini try ve catch ile engellemeyi, throw ile kendi hata mesajlarını üretmeyi öğreneceksin.

İşaret yalnızca bu tarayıcıda saklanır, kimseye gönderilmez.