---
title: 'JavaScript Temelleri'
source: 'https://academia.sh/tr/kurslar/javascript-temelleri'
language: tr
updated: '2026-08-17T04:41:32+00:00'
license: 'CC BY-SA 4.0'
lessons: 21
outcomes:
    - 'Değişken bildirimi, tip dönüşümü ve eşitlik kurallarını doğru kullanır'
    - 'Temel veri yapılarıyla (dizi, nesne) veri dönüşümü yazar'
    - 'Betiği tarayıcıda ve çalışma zamanında çalıştırıp hatalarını okur'
---

# JavaScript Temelleri

Dilin değer modeli, bildirim ve kapsam kuralları, tip zorlama ve eşitlik algoritmaları ile yerleşik veri yapıları; betiği çalıştırıp hatalarını okuma.

## Dilin Yapısı

Dil, çalışma zamanı ve standart ayrımı; kodu çalıştırma yolları; bildirim türlerinin kapsam farkı ve yukarı taşıma.

### JavaScript Nedir

Source: https://academia.sh/tr/kurslar/javascript-temelleri/javascript-nedir

Dil, motor, çalışma zamanı ve standart ayrımı; ECMAScript'in tanımladığı ile barındıran ortamın eklediği arasındaki sınır.

Programlama Temelleri kursunda değişken, ifade, koşul, döngü ve fonksiyon kavramları
dilden bağımsız olarak kuruldu. Bu kurs aynı kavramların tek bir dildeki karşılığını
değil, o dilin kendine özgü kararlarını inceler: bir adın hangi kapsamda görüldüğünü,
iki değerin hangi kurala göre eşit sayıldığını, bir tipin başka bir tipe hangi adımlarla
dönüştürüldüğünü.

Bu incelemeye başlamadan önce bir ayrım gerekir. "JavaScript" adı günlük konuşmada
birbirinden farklı üç şeyi birden karşılar: bir dil, o dili yürüten bir program ve o
programın çevresindeki hizmetler. Bu dersin konusu bu üçünün sınırıdır. Sınır
bilinmediğinde, bir tarayıcıda çalışan betiğin sunucuda neden çalışmadığı sorusunun
yanıtı da bulunamaz.

#### Dil, Motor ve Çalışma Zamanı

**Dil**, sözdizimi ve anlambilimidir: hangi karakter dizilerinin geçerli program olduğu
ve geçerli bir programın ne anlama geldiği. `const`, `+`, `if` gibi yapılar ve bunların
davranışı dile aittir.

**Motor (engine)**, dili yürüten programdır. Kaynak metni ayrıştırır, iç gösterime
çevirir ve çalıştırır. Bilgisayarlar Nasıl Çalışır kursundaki Derleyici, Yorumlayıcı ve
Sanal Makine dersinde tanımlanan karma model burada geçerlidir: kaynak önce bayt koduna
çevrilir, sık çalışan bölümler ise anında derleme ile makine koduna dönüştürülür. Bu
nedenle "JavaScript yorumlanan bir dildir" cümlesi eksiktir; yürütme modeli motorun
kararına bağlıdır ve dil tanımının bir parçası değildir.

**Çalışma zamanı (runtime)**, motorun etrafındaki barındıran ortamdır (host
environment). Motor tek başına dosya okuyamaz, ekrana yazamaz, ağa bağlanamaz. Bu
yetenekleri barındıran ortam sağlar. Tarayıcı bir barındıran ortamdır; belge nesnelerini
ve sayfa olaylarını sunar. Sunucu tarafı çalışma zamanı da bir barındıran ortamdır;
dosya sistemi ve ağ soketleri sunar.

Üç katman birbirinden bağımsız değişebilir. Aynı dil, farklı motorlarla yürütülebilir;
aynı motor, farklı barındıran ortamların içine yerleştirilebilir.

#### Standart Ne Söyler, Ne Söylemez

Dilin tanımı **ECMAScript** adlı standarttır. Bu ad ile "JavaScript" arasındaki ilişki
tarihseldir; kurs boyunca dilin kendisi kastedildiğinde **JavaScript**, standardın
belirlediği kural kastedildiğinde **ECMAScript** yazılacaktır.

Standardın kapsamı bellidir:

- Sözdizimi ve dilbilgisi.
- Değer tipleri ve bunlar üzerindeki işlemler.
- Yerleşik nesneler: `Object`, `Array`, `String`, `Math`, `JSON`, `Date`, `Map`, `Set`
  ve benzerleri.
- Soyut işlemler: iki değerin nasıl karşılaştırılacağı, bir değerin sayıya nasıl
  çevrileceği gibi adım adım tanımlı yordamlar. Bu kursun Değerler ve Tipler konusu
  büyük ölçüde bu yordamları okur.

Standardın kapsamı dışında kalanlar en az bunlar kadar önemlidir. Ekrana yazma, dosya
okuma, ağ isteği gönderme, zamanlayıcı kurma ve belgeye erişme ECMAScript'te tanımlı
değildir. Bu işlerin tümü barındıran ortamdan gelir. `console` nesnesi bile standardın
parçası değildir; yaygın olarak bulunmasının nedeni standart olması değil, barındıran
ortamların onu sağlamayı seçmesidir.

#### Bir Adın Nereden Geldiğini Sınamak

Ayrım soyut kalmasın: bir adın hangi katmandan geldiği doğrudan sınanabilir. `typeof`
işleci, tanımsız bir ad için hata vermeden `"undefined"` üretir; bu yüzden yoklama için
uygundur.

```js
console.log(typeof globalThis.Math);      // standarttan gelir
console.log(typeof globalThis.JSON);      // standarttan gelir
console.log(typeof globalThis.console);   // barındıran ortamdan gelir
console.log(typeof globalThis.document);  // yalnızca belge tabanlı ortamda bulunur
```

Sunucu tarafı çalışma zamanında çalıştırıldığında:

```
object
object
object
undefined
```

`Math` ve `JSON` her ECMAScript ortamında bulunur. `console` bu ortamda vardır ama
standarttan değil, ortamın kendisinden gelir. `document` ise yoktur: belge nesnesi,
sayfayı gösteren bir ortamın sunduğu bir şeydir. Aynı dört satır tarayıcı konsolunda
çalıştırıldığında dördüncü satır `object` üretir; ilk üçü değişmez.

`globalThis`, o ortamdaki genel nesneye taşınabilir biçimde erişmenin adıdır. Genel
nesnenin adı ortamlar arasında değiştiği için, taşınabilir kod bu adı kullanır.

Buradan pratik bir kural çıkar: bir betik "her yerde çalışır" olacaksa, yalnızca
standardın tanımladığı adlara dayanmalıdır. Dosya okuma veya ekrana yazma gerektiği anda
betik bir barındıran ortama bağlanmış olur.

#### Betik Nasıl Yürütülür

Bir dosya çalıştırıldığında iki ayrı aşama vardır ve ikisi arasındaki fark, hata
iletilerini okumanın temelidir.

Önce **ayrıştırma** yapılır. Kaynak metnin tamamı dilbilgisine göre çözümlenir. Bu
aşamada bulunan hata bir sözdizim hatasıdır ve programın hiçbir satırı çalışmaz:

```js
console.log("bu satir yazilir mi?");
const x = ;
```

Bu dosya çalıştırıldığında `bu satir yazilir mi?` **yazılmaz**. Çıktı yalnızca hatayı
bildirir; iletinin özü şudur:

```
SyntaxError: Unexpected token ';'
```

Ayrıştırma başarılıysa **yürütme** başlar. Bu aşamada ortaya çıkan hata çalışma zamanı
hatasıdır ve kendisinden önceki satırlar çalışmış olur:

```js
console.log("bu satir yazilir");
const kayit = null;
console.log(kayit.sicaklik);
```

```
bu satir yazilir
```

Ardından yürütme durur ve şu ileti verilir:

```
TypeError: Cannot read properties of null (reading 'sicaklik')
```

Her iki durumda da çalışma zamanı, iletiden önce dosyanın tam yolunu, hatanın bulunduğu
satırı ve bir yığıt izini (stack trace) yazar. Bu satırlar makineye ve sürüme göre
değiştiği için burada gösterilmemiştir; kursta hata çıktılarının yalnızca değişmeyen
bölümü verilecektir. Hata iletisini okumanın yolu Kod Çalıştırma Yolları dersinde
ayrıntılanır.

Programlama Temelleri kursunun Program Nedir dersinde kurulan üçlü ayrım — sözdizim
hatası, çalışma zamanı hatası, mantık hatası — burada birebir geçerlidir. Değişen tek
şey, hatanın hangi aşamada göründüğünün somut olarak gözlenebilmesidir.

#### Kursun Ekseni: Ölçüm Kayıtları

Kurs boyunca tek bir somut problem üzerinde çalışılacak: bir ölçüm istasyonu
kümesinden gelen okumaların listesi. Bu liste ders ders büyüyecek; önce yalın sayılar,
sonra alanları olan kayıtlar, sonunda metinden okunup dönüştürülen ve yeniden metne
yazılan bir veri kümesi olacak.

İlk hali dört sıcaklık okumasından ibarettir:

```js
const sicakliklar = [21.5, 19.75, 23, 18.25];
let toplam = 0;
for (const deger of sicakliklar) {
  toplam += deger;
}
console.log("okuma sayisi:", sicakliklar.length);
console.log("en yuksek:", Math.max(...sicakliklar));
console.log("ortalama:", toplam / sicakliklar.length);
```

```
okuma sayisi: 4
en yuksek: 23
ortalama: 20.625
```

Bu betikteki her ad iki kaynaktan birine aittir. `const`, `let`, `for` ve `of` dile;
`Array` ve `Math` standardın yerleşik nesnelerine; `console` ise barındıran ortama.
Betiği tarayıcıda çalıştırmak yalnızca son bağı değiştirir — dilin ve yerleşik
nesnelerin davranışı aynıdır.

Çıktıdaki `23` değerine dikkat edin: kaynakta `23` yazıldı ve `23` yazıldı, `23.0`
değil. Sayı gösteriminin bu ayrıntısı Değerler ve Tipler konusunun konusudur ve
Bilgisayarlar Nasıl Çalışır kursundaki Kayan Noktalı Sayılar dersinin doğrudan
sonucudur.

#### Özet

- "JavaScript" adı üç katmanı birden anlatır: dil, dili yürüten motor ve motorun
  çevresindeki barındıran ortam. Üçü bağımsız değişebilir.
- Dilin tanımı ECMAScript standardıdır; standart sözdizimi, değer tiplerini, yerleşik
  nesneleri ve soyut işlemleri kapsar.
- Ekrana yazma, dosya okuma, ağ erişimi ve belge nesneleri standartta yoktur; barındıran
  ortamdan gelir. `console` bile bu sınıftadır.
- `typeof globalThis.ad` yoklaması, bir adın hangi katmandan geldiğini hata vermeden
  sınar.
- Ayrıştırma hatası hiçbir satırı çalıştırmaz; çalışma zamanı hatası kendisinden önceki
  satırlar çalıştıktan sonra ortaya çıkar.

#### Sonraki Adım

Betiğin nerede çalıştığı, hangi adların bulunduğunu belirliyor. Sonraki ders bu
çalıştırma yollarını tek tek ele alacak: tarayıcı konsolu, sayfaya bağlanan betik
etiketi ve komut satırından çağrılan sunucu tarafı çalışma zamanı. Aynı ölçüm betiği üç
yolda da çalıştırılacak; aralarındaki farkın nereden geldiği ve bir hata iletisinin
hangi bölümünün okunmaya değer olduğu görülecek.

### Kod Çalıştırma Yolları

Source: https://academia.sh/tr/kurslar/javascript-temelleri/kod-calistirma-yollari

Dosyadan çalıştırma, etkileşimli kabuk, tarayıcı konsolu ve betik etiketi; betik ile modül ayrımı ve hata iletisinin okunması.

Önceki ders, bir betiğin hangi adları görebildiğinin barındıran ortama bağlı olduğunu
gösterdi. Bu ders, o ortamlara kodun nasıl verildiğini ele alır: komut satırından bir
dosya, etkileşimli bir kabuk oturumu, tarayıcı konsolu ve sayfaya bağlanan bir betik
etiketi. Dört yol yalnızca "kodu başlatma" biçimi değildir; her biri kodun hangi kurallar
altında yorumlanacağını da belirler.

Aynı ölçüm betiği bu yolların hepsinde çalıştırılacak. Sonuçların örtüştüğü ve
ayrıştığı noktalar, dilin çekirdeği ile ortamın katkısı arasındaki sınırı ikinci kez
çizer.

#### Komut Satırından Bir Dosya Çalıştırmak

Sunucu tarafı çalışma zamanı, kaynağı bir dosyadan alır. Kurstaki örnekler `node`
komutuyla çalıştırılmıştır; dosyaya verilen ad ve uzantı, kaynağın nasıl yorumlanacağını
belirler.

Aşağıdaki kodu `olcum.mjs` adlı bir dosyaya kaydedelim:

```js
// olcum.mjs
const sicakliklar = [21.5, 19.75, 23, 18.25];
let toplam = 0;
for (const deger of sicakliklar) {
  toplam += deger;
}
console.log("okuma sayisi:", sicakliklar.length);
console.log("ortalama:", toplam / sicakliklar.length);
```

Dosyayı kabukta `node` ile çalıştıralım:

```sh
node olcum.mjs
```

```
okuma sayisi: 4
ortalama: 20.625
```

Dosya baştan sona okunur, ayrıştırılır ve yürütülür. Süreç, kodun son satırı bittikten
sonra bekleyen bir iş kalmadıysa sona erer.

Tek satırlık denemeler için dosya açmak gerekmez; kaynak doğrudan argüman olarak
verilebilir:

```sh
node -e 'console.log(Math.max(21.5, 19.75, 23))'
```

```
23
```

#### Etkileşimli Kabuk

Argümansız çağrıldığında çalışma zamanı bir **etkileşimli kabuk (REPL)** açar. Bu
kabuk her satırı okur, değerlendirir, sonucu yazar ve döngüye döner. Adı bu dört adımdan
gelir.

```
> const sicakliklar = [21.5, 19.75, 23, 18.25];
undefined
> sicakliklar.length
4
> Math.max(...sicakliklar)
23
> typeof sicakliklar
'object'
```

İki gözlem bu kabuğu anlamak için yeterlidir.

Birincisi: kabuk, her satırın **değerini** yazar. Bir bildirim deyimi değer üretmez;
bu yüzden ilk satırın karşılığı `undefined` olmuştur. Bu, bildirimin başarısız olduğu
anlamına gelmez — sonraki satır değişkeni sorunsuz okuyor. Programlama Temelleri
kursundaki deyim–ifade ayrımı burada gözle görülür hale gelir: `const` bir deyimdir,
`sicakliklar.length` bir ifadedir.

İkincisi: kabuğun yazdığı biçim ile `console.log` biçimi aynı değildir. `typeof`
sonucu kabukta `'object'` olarak, tırnaklarla yazıldı; aynı değer `console.log` ile
yazılsaydı tırnaksız `object` görünürdü. Kabuk değeri **inceleme biçiminde** yazar:
metinler tırnaklanır, böylece `4` sayısı ile `'4'` metni ayırt edilebilir. Bu biçimin
ayrıntıları çalışma zamanına özgüdür ve standartta tanımlı değildir.

Aynı fark nesnelerde de görülür:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5 };
console.log(kayit);
console.log(String(kayit));
```

```
{ istasyon: 'A1', sicaklik: 21.5 }
[object Object]
```

İlk satırdaki okunabilir gösterim, ortamın inceleme biçimidir. İkinci satır ise dilin
kendi metne çevirme kuralının sonucudur ve her ortamda aynıdır. Tarayıcı konsolu ilk
satır için katlanıp açılabilen bir ağaç gösterir; ikinci satır orada da `[object Object]`
üretir.

#### Tarayıcı Konsolu ve Betik Etiketi

Belge tabanlı ortamda kod iki yoldan girer. **Konsol**, sayfanın bağlamında açılmış bir
etkileşimli kabuktur; yukarıdaki kuralların aynısı geçerlidir. **Betik etiketi** ise
kodu sayfanın yüklenme sürecine bağlar:

```html
<script src="olcum.js"></script>
```

Etiketin konumu ve nitelikleri yürütme anını belirler. Nitelik verilmezse belge
çözümlemesi durur, kaynak indirilir ve çalıştırılır, sonra çözümleme sürer. `defer`
niteliği yürütmeyi belge çözümlemesinin sonuna erteler; `type="module"` niteliği hem
aynı ertelemeyi uygular hem de kaynağı betik değil modül olarak yükler.

Bu kursun örnekleri konsolda ve komut satırında doğrulanabilir; belge nesnelerine
dokunmadıkları için sayfa bağlamı gerektirmezler. Belgeye erişen kod, dilin değil
barındıran ortamın konusudur.

#### Betik mi, Modül mü

ECMAScript, bir kaynak metni iki farklı hedefe göre ayrıştırır: **betik (script)** ve
**modül (module)**. Bu, dosya uzantısından ya da etiket niteliğinden çıkarılan bir
karardır ve üç somut fark üretir.

Birinci fark **katı kiptir (strict mode)**. Modül olarak yüklenen kaynak her zaman katı
kipte çalışır; betik olarak yüklenen kaynak, ayrıca istenmedikçe çalışmaz. Katı kip,
sessizce yanlış sonuç üreten birkaç davranışı hataya çevirir. En görünür örneği,
bildirilmemiş bir ada atama yapmaktır:

```js
try {
  bildirilmemis = 5;
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

Modül olarak (`.mjs` uzantısıyla) çalıştırıldığında:

```
ReferenceError: bildirilmemis is not defined
```

Aynı atama katı kipte olmayan bir betikte hata vermez; sessizce genel bir ad yaratır.
Yazım hatasıyla oluşmuş bir adın program boyunca yaşaması bu yüzden mümkündür.

İkinci fark **en dış kapsamdaki `this` değeridir**. Modülde bu değer `undefined`'dır;
betikte ise ortamın seçtiği bir nesnedir.

Üçüncü fark, en dış kapsamda bildirilen adların **genel nesneye yazılıp
yazılmadığıdır**. Modülde en dış kapsam modüle özeldir; orada bildirilen hiçbir ad genel
nesnenin özelliği olmaz:

```js
var x = 1;
console.log(typeof this);
console.log(typeof globalThis.x);
```

Modül olarak çalıştırıldığında:

```
undefined
undefined
```

Klasik betik olarak yüklenen bir kaynakta ise `var` ile bildirilen en dış kapsam adları
genel nesnenin özelliği olur. İki dosyanın birbirinin adlarını kazara ezmesi bu
davranışın sonucudur; modül modeli bu paylaşımı ortadan kaldırır.

Kurs boyunca örnekler modül olarak çalıştırılmıştır. Bir örnek yalnızca betik kipinde
farklı davranıyorsa, bu metinde ayrıca belirtilecektir.

#### Hata İletisini Okumak

Yakalanmamış bir hata üç parça bilgi taşır: hatanın **adı**, **iletisi** ve **yığıt
izi**. Üçü ayrı ayrı okunabilir:

```js
const kayitlar = [{ istasyon: "A1", sicaklik: 21.5 }];
try {
  console.log(kayitlar[1].sicaklik);
} catch (hata) {
  console.log("ad   :", hata.name);
  console.log("ileti:", hata.message);
}
```

```
ad   : TypeError
ileti: Cannot read properties of undefined (reading 'sicaklik')
```

**Ad**, hatanın sınıfını verir. `TypeError` bir değerin beklenen türde olmadığını,
`ReferenceError` bir adın bulunamadığını, `SyntaxError` kaynağın ayrıştırılamadığını
bildirir.

**İleti**, olayın ayrıntısıdır. Yukarıdaki iletiyi adım adım okumak, hatanın nerede
olduğunu doğrudan söyler: `kayitlar[1]` değeri `undefined` çıkmıştır ve `undefined`
üzerinde `sicaklik` özelliği aranmıştır. Yani asıl sorun, dizinin ikinci elemanının
bulunmamasıdır — özelliğin adı değil. Hata iletisi, hatanın **belirtisini** gösterir;
nedeni genellikle bir üst satırdadır.

**Yığıt izi**, hatanın hangi çağrı zincirinden geçtiğini `at` ile başlayan satırlarda
listeler. En üstteki satır hatanın oluştuğu yerdir; alt satırlar oraya kimin çağırdığını
verir. Yığıt izinin biçimi, dosya yolları ve çalışma zamanının iç çerçeveleri makineye
göre değiştiği için bu kursta yalnızca ad ve ileti bölümleri gösterilecektir.

Yakalanmamış bir hata, sunucu tarafı çalışma zamanında süreci sıfırdan farklı bir çıkış
koduyla sonlandırır. Tarayıcıda ise sayfa çalışmaya devam eder; hata yalnızca konsola
yazılır. Aynı hatanın iki ortamdaki sonucu farklıdır, ama iletisi aynıdır.

#### Özet

- Kod dört yoldan girer: dosya, etkileşimli kabuk, tarayıcı konsolu ve belge
  bağlamındaki betik etiketi.
- Etkileşimli kabuk her satırın değerini inceleme biçiminde yazar; bildirimler değer
  üretmediği için `undefined` görünür. Bu biçim standarttan değil ortamdan gelir.
- ECMAScript bir kaynağı betik ya da modül olarak ayrıştırır; modül her zaman katı
  kiptedir, en dış `this` değeri `undefined`'dır ve adları genel nesneye yazılmaz.
- Katı kip, bildirilmemiş ada atamayı sessiz bir genel ad yaratmak yerine
  `ReferenceError`'a çevirir.
- Hata iletisi ad, ileti ve yığıt izinden oluşur; ileti belirtiyi anlatır, neden çoğu
  zaman bir önceki adımdadır.

#### Sonraki Adım

Betiğin modül olarak yüklenmesi, `const` ve `let` bildirimlerinin varlığı, `for...of`
döngüsü — bunların hepsi dilin belirli bir noktada kazandığı yeteneklerdir. Sonraki ders,
bir özelliğin dile nasıl girdiğini ele alacak: standardın sürümlenme biçimi, önerilerin
geçtiği aşamalar ve "bu özellik burada var mı" sorusunun sürüm adına bakmadan nasıl
yanıtlanacağı.

### Dil Sürümleri ve Standartlaşma

Source: https://academia.sh/tr/kurslar/javascript-temelleri/dil-surumleri-ve-standartlasma

ECMAScript baskılarının sürümlenmesi, öneri aşamaları, geriye dönük uyumluluğun dile bıraktığı izler ve özellik saptama.

Önceki derste modül kipi, `const` bildirimi ve `for...of` döngüsü kullanıldı. Bunların
hiçbiri dilin ilk halinde yoktu; her biri standarda belirli bir baskıyla girdi. Bir
özelliğin "dilde olması" ile "elinizdeki ortamda çalışması" ayrı sorulardır ve ikisinin
yanıtı farklı yerlerden gelir.

Bu ders, standardın nasıl sürümlendiğini, bir önerinin hangi aşamalardan geçtiğini ve
uyumluluk kaygısının dile hangi kalıcı izleri bıraktığını ele alır. Sonunda pratik bir
teknik kurulur: bir yeteneğin varlığını sürüm adına bakmadan sınamak.

#### Standardın Sürümlenmesi

Dilin tanımı, ECMA-262 numaralı belgedir. Belge **baskı (edition)** olarak sürümlenir;
bir baskı, o noktada standarda alınmış bütün özellikleri içeren tam bir metindir.
Baskılar önceki baskıların üzerine ekleme yapar; yayımlanmış bir özellik sonraki
baskıda kaldırılmaz.

Baskılar ECMAScript 2015 ile birlikte yıllık bir düzene geçti ve o baskıdan itibaren
yayım yılıyla anılır. Bu, teknik bir adlandırma sözleşmesidir: "ES2015 itibarıyla blok
kapsamlı bildirim vardır" cümlesi, hangi metinde tanımlandığını söyler. Bir baskının adı
o özelliğin nerede kullanılabildiği hakkında hiçbir şey söylemez; o soru ortamın
sorusudur.

Sürüm adları bu kursta yalnızca bir davranışın kaynağını göstermek gerektiğinde
kullanılacaktır. Örneğin "blok kapsamı ES2015 ile geldi" cümlesi, `var` ile `let`
arasındaki farkın neden var olduğunu açıklar.

#### Bir Özellik Dile Nasıl Girer

Öneriler, dili sürdüren teknik komitede aşamalı bir süreçten geçer. Aşamalar sıfırdan
dörde numaralanır ve her aşamanın giriş ölçütü vardır:

- **Aşama 0 — fikir.** Komiteye sunulmuş, biçimlendirilmemiş bir öneri.
- **Aşama 1 — öneri.** Çözülmek istenen problem, örnek kullanım ve olası zorluklar
  yazılıdır. Komite problemi ele almaya değer bulmuştur.
- **Aşama 2 — taslak.** Sözdizimi ve anlambilim, standardın diliyle biçimsel olarak
  yazılmıştır. Bu aşamadan sonra yapılan değişiklikler ayrıntı düzeyindedir.
- **Aşama 3 — aday.** Metin tamam, sınamalar yazılmıştır; sıra gerçekleştirim ve kullanım
  geri bildirimine gelmiştir. Gerçekleştirimler bu aşamada toplanır; koşul olarak
  aranmaları bir sonraki aşamadadır.
- **Aşama 4 — tamamlanmış.** Bağımsız gerçekleştirimler ve uyum sınamaları hazırdır.
  Öneri bir sonraki baskıya girer.

Sürecin iki sonucu vardır. Birincisi, bir özellik standarda girmeden önce bazı
ortamlarda çalışabilir; ikincisi, aşama 4'e ulaşmamış bir öneri hâlâ değişebilir.
"Sözdizimi şu anda şöyle çalışıyor" gözlemi, o sözdiziminin kalıcı olduğunu göstermez.

#### Geriye Dönük Uyumluluğun Bedeli

Standardın taşıdığı en güçlü kısıt, daha önce yayımlanmış sayfaların çalışmaya devam
etmesidir. Bu kısıt, hatalı olduğu kabul edilen davranışların bile düzeltilmemesine yol
açar. Programlama Temelleri kursundaki Bir Dil Seçmek dersinde tanımlanan geriye dönük
uyumluluk kavramının en görünür örneklerinden biridir.

```js
console.log(typeof null);
console.log([] + {});
```

```
object
[object Object]
```

İlk satırdaki sonuç bir tanım gereği değil, dilin ilk gerçekleştirimindeki bir
kodlamanın sonucudur. `null` bir nesne değildir; ama `typeof null` ifadesinin `"null"`
üretmesi, bu ifadenin sonucuna dayanan kodları bozardı. İkinci satır, tip zorlama
kurallarının bir dizi ile bir nesneyi metne çevirip birleştirmesidir; Tip Dönüşümü ve
Zorlama dersinde adım adım çözümlenecektir.

Bu davranışlar "tuhaflık" olarak değil, **kural** olarak öğrenilmelidir. Her biri
standartta açıkça yazılıdır ve adım adım izlenebilir. Kursun Değerler ve Tipler konusu
bu adımları okur.

Uyumluluk kısıtının ikinci sonucu, yeni yeteneklerin çoğunlukla **ekleme** biçiminde
gelmesidir: eski davranış kaldırılmaz, yanına yeni bir yol açılır. `var` bildiriminin
yanına `let` ve `const` gelmesi bu kalıbın örneğidir; eskisi çalışmayı sürdürür, yenisi
farklı bir kapsam kuralı sunar. İki bildirim türü arasındaki fark bir sonraki dersin
konusudur.

#### Özellik Saptama

"Bu ortamda şu yetenek var mı" sorusu, sürüm adına bakarak yanıtlanmaz. Bir ortamın
hangi baskıyı ne ölçüde uyguladığı, ortamın kendisine sorularak öğrenilir. Yerleşik
nesnelere eklenen yöntemler için yoklama doğrudandır:

```js
console.log(typeof Array.prototype.map);
console.log(typeof Array.prototype.at);
console.log(typeof Object.groupBy);
```

Örneklerin çalıştırıldığı ortamda üçü de bulunur:

```
function
function
function
```

Aynı yoklama, yeteneği bulunmayan bir ortamda `undefined` üretir. Bu, yedekli kod
yazmanın temelidir:

```js
const sicakliklar = [21.5, 19.75, 23, 18.25];

function sonEleman(dizi) {
  if (typeof dizi.at === "function") {
    return dizi.at(-1);
  }
  return dizi[dizi.length - 1];
}

console.log(sonEleman(sicakliklar));
```

```
18.25
```

Yoklamanın `typeof` ile yapılması önemlidir: bildirilmemiş bir ada doğrudan bakmak
`ReferenceError` verirken, `typeof` hata vermeden `"undefined"` üretir. Nesne
özelliklerinde bu risk yoktur, ama alışkanlığı tek biçimde tutmak yanlış yoklamayı
önler.

#### Eksik Yeteneği Tamamlamak

Saptama, eksikliği bildirir; onu gidermez. Eksik bir yeteneğin yerine geçen koda **yetenek
dolgusu (polyfill)** denir. Yetenek dolgusu, yalnızca yetenek yoksa tanımlanmalı ve
var olan gerçekleştirimi
ezmemelidir.

```js
const yerlesikVar = typeof Array.prototype.at === "function";
console.log("at yerleşiği:", yerlesikVar);

if (typeof Array.prototype.sonuncu !== "function") {
  Object.defineProperty(Array.prototype, "sonuncu", {
    value: function () { return this[this.length - 1]; },
    writable: true, configurable: true, enumerable: false,
  });
}
console.log([3, 7, 9].sonuncu());

const anahtarlar = [];
for (const k in [1, 2]) anahtarlar.push(k);
console.log("for...in anahtarları:", anahtarlar);
```

```
at yerleşiği: true
9
for...in anahtarları: [ '0', '1' ]
```

Üçüncü satır, yetenek dolgusunun neden doğrudan atamayla yazılmadığını gösterir. Prototipe düz
atamayla eklenen bir özellik sayılabilir olur ve `for...in` gezinmesine karışır; buradaki
tanımlama `enumerable: false` verdiği için dizinin anahtarları değişmeden kalır.

İki kural buradan çıkar. Birincisi, yerleşik bir yetenek varsa ona dokunulmaz —
davranışı standarttakinden farklı bir yetenek dolgusu, sonradan gelen kodun
varsayımlarını bozar. İkincisi, standart olmayan bir ad ekleniyorsa (`sonuncu` gibi)
bunun standartla çakışma riski taşıdığı bilinmelidir; ileride aynı ad standarda girerse
iki tanım karşı karşıya gelir.

#### Sözdizimi Neden Saptanamaz

Yöntem yoklaması yalnızca **çalışma zamanında var olan** şeyler için işe yarar. Yeni bir
sözdizimi bu yolla sınanamaz, çünkü dosyanın tamamı yürütmeden önce ayrıştırılır. İlk
derste görüldüğü gibi, ayrıştırma hatası dosyanın hiçbir satırını çalıştırmaz. Bir
sözdizimini tanımayan ortamda, o sözdizimini içeren dosya bütünüyle reddedilir; içine
yazılmış bir denetim de çalışma fırsatı bulamaz.

Bu yüzden yetenek eksiğini kapatmanın iki ayrı araç sınıfı vardır:

- **Kaynaktan kaynağa çevirici (transpiler)**, yeni sözdizimini eski sözdizimine
  çevirir. Ayrıştırma sorununu kaynağı değiştirerek çözer.
- **Dolgu (polyfill)**, eksik yerleşik yöntemi çalışma zamanında tanımlar. Sözdizimini
  değiştiremez; yalnızca eksik nesne ve yöntemleri ekler.

İkisi farklı problemleri çözer ve birbirinin yerine geçmez. Bu kurs her ikisini de
kullanmaz; örnekler doğrudan çalıştırılabilecek sözdizimiyle yazılmıştır.

#### Standardın Kendisini Okumak

Standart metni bir öğretim kitabı değildir; kesin bir tanım belgesidir. Yapısı
öğrenildiğinde, "bu ifade neden bu sonucu verdi" sorusunun tartışmasız yanıtını verir.
Metinde her işleç ve her yerleşik yöntem, numaralanmış adımlarla tanımlanmış bir **soyut
işlem** çağrısına indirgenir. Örneğin toplama işleci, önce her iki işleneni ilkel değere
çeviren bir soyut işlemi çağırır; sonucun sayı toplaması mı yoksa metin birleştirmesi mi
olacağına o adımın sonucuna göre karar verir.

Kursun Değerler ve Tipler konusu bu adımları Türkçeleştirerek izleyecek. Amaç, sonucu
ezberlemek değil, sonucu üreten adımları takip edebilmektir. Bir kez izlendiğinde,
karşılaşılmamış bir ifadenin sonucu da aynı yöntemle bulunabilir.

#### Özet

- Dilin tanımı ECMA-262 belgesidir; baskılar birikimlidir ve yayımlanmış özellikler
  kaldırılmaz.
- Öneriler sıfırdan dörde numaralanmış beş aşamadan geçer; aşama 4'e ulaşan öneri bir
  sonraki baskıya girer.
- Geriye dönük uyumluluk, hatalı kabul edilen davranışların da korunmasına yol açar;
  `typeof null` sonucunun `"object"` olması bunun örneğidir.
- Yeni yetenekler çoğunlukla eskisinin yerine değil yanına eklenir; `var` yanında `let`
  ve `const` bulunması bu kalıptır.
- Yerleşik bir yeteneğin varlığı `typeof` yoklamasıyla sınanır; sözdizimi ise
  ayrıştırma aşamasında değerlendirildiği için çalışma zamanında sınanamaz.

#### Sonraki Adım

Bildirim türlerinin yan yana durması bu dersin son gözlemiydi. Sonraki ders bu üç
bildirimi karşılaştırır: `var`, `let` ve `const` arasındaki fark yalnızca yazım tercihi
değildir; adın hangi bölgede görüldüğünü, ne zaman erişilebilir olduğunu ve yeniden
bağlanıp bağlanamayacağını belirler. Ölçüm betiği bu farkın gözlemlenebildiği bir
biçime getirilecek.

### Değişken Bildirimleri

Source: https://academia.sh/tr/kurslar/javascript-temelleri/degisken-bildirimleri

Fonksiyon kapsamlı ve blok kapsamlı bildirimlerin farkı, yeniden bildirim kuralları, sabit bağlamanın kapsamı ve döngü değişkeninin bağlanması.

Önceki ders, yeni yeteneklerin eskisinin yerine değil yanına eklendiğini söyledi. Bunun
en somut örneği bildirim türleridir: dilin ilk halinde tek bir bildirim vardı, ES2015 ile
iki bildirim daha eklendi ve eskisi kaldırılmadı. Üçü aynı işi yapmaz.

Programlama Temelleri kursundaki Kapsam ve Yaşam Süresi dersinde kapsam, bir adın
görüldüğü program bölgesi olarak tanımlanmıştı. Bu ders o tanımı JavaScript'e uygular ve
üç bildirimin hangi bölgeyi seçtiğini gösterir. Fark yazım tercihi değildir; adın nerede
görüleceğini, ne zaman erişilebilir olacağını ve yeniden bağlanıp bağlanamayacağını
belirler.

#### İki Kapsam Birimi

`var` ile bildirilen ad **fonksiyon kapsamlıdır**: içinde bulunduğu fonksiyonun
tamamında görülür. Bildirimin bir blok içinde yapılmış olması bir şeyi değiştirmez.

`let` ve `const` ile bildirilen ad **blok kapsamlıdır**: yalnızca içinde bulunduğu süslü
ayraç çiftinde ve onun içindeki bloklarda görülür.

```js
function kapsamDenemesi() {
  if (true) {
    var varDegisken = "var ile";
    let letDegisken = "let ile";
    console.log(varDegisken, "/", letDegisken);
  }
  console.log(varDegisken);
  console.log(typeof letDegisken);
}
kapsamDenemesi();
```

```
var ile / let ile
var ile
undefined
```

Blok kapandıktan sonra `var` ile bildirilen ad hâlâ erişilebilir; `let` ile bildirilen ad
görünmez olmuştur. Üçüncü satırın `typeof` ile yazılmasının nedeni budur: doğrudan
okumak hata verirdi.

Hatanın kendisini görmek, ayrımın keskinliğini gösterir:

```js
if (true) {
  let esik = 20;
  console.log(esik);
}
try {
  console.log(esik);
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
20
ReferenceError: esik is not defined
```

Blok kapsamı, adın ömrünü ihtiyaç duyulan bölgeyle sınırlar. Programlama Temelleri
kursundaki gölgeleme ve ad çakışması tartışması burada doğrudan işe yarar: bir adın
görünürlük alanı ne kadar darsa, kazara ezilme olasılığı o kadar azdır.

#### Yeniden Bildirim

Üç bildirim, aynı adın ikinci kez bildirilmesine farklı tepki verir.

`var`, aynı kapsamda aynı adın yeniden bildirilmesine izin verir. İkinci bildirim yeni
bir ad yaratmaz; var olan ada yeni bir değer atar:

```js
var esik = 20;
var esik = 25;
console.log(esik);
```

```
25
```

Bu esneklik, uzun bir fonksiyonun ortasında farkında olmadan aynı adı ikinci kez
kullanmayı sessiz bir hataya çevirir. `let` ve `const` aynı kapsamda yeniden bildirimi
reddeder:

```js
let esik = 20;
let esik = 25;
```

Bu dosya çalıştırıldığında hiçbir satırı yürütülmez:

```
SyntaxError: Identifier 'esik' has already been declared
```

Hatanın türüne dikkat edin. Bu bir çalışma zamanı hatası değil, ayrıştırma hatasıdır;
ilk derste kurulan ayrım burada işe yarıyor. Aynı adın iki kez bildirilmesi, kaynağın
tamamı okunurken saptanabilir bir durumdur, bu yüzden program hiç başlamaz.

`const` bildirimi ayrıca başlangıç değeri ister:

```js
const esik;
```

```
SyntaxError: Missing initializer in const declaration
```

Bunun nedeni sonraki bölümde görülecek: sabit bir ad yalnızca bir kez bağlanabildiği
için, bağlanmamış bir sabit hiçbir zaman değer alamazdı.

#### Sabit Bildirimin Sınırı

`const`, **bağlamayı** sabitler. Programlama Temelleri kursundaki Değişkenler ve Bağlama
dersinde ad ile değer arasındaki ilişki "bağlama" olarak adlandırılmıştı; `const` tam
olarak o ilişkinin değişmesini engeller.

```js
const esik = 20;
try {
  esik = 21;
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError: Assignment to constant variable.
```

Sabitlenen şey bağlamadır, değerin içeriği değil. Bağlanan değer değiştirilebilir bir
nesneyse, nesnenin içeriği değişebilir:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5 };
kayit.sicaklik = 22;
console.log(kayit);
```

```
{ istasyon: 'A1', sicaklik: 22 }
```

Ad hâlâ aynı nesneye bağlıdır; değişen, nesnenin bir özelliğidir. Aynı ada başka bir
nesne bağlamak ise reddedilir:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5 };
try {
  kayit = { istasyon: "A2", sicaklik: 19 };
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError: Assignment to constant variable.
```

Bu ayrım terminolojiyle örtüşür: `const` bir **sabit** (constant) bağlama kurar, değeri
**değiştirilemez** (immutable) yapmaz. Nesnelerin içeriğini gerçekten dondurmanın yolu
ayrı bir işlemdir ve bu kursun kapsamı dışındadır.

#### Döngü Değişkeninin Bağlanması

Kapsam farkının en çok sonuç doğurduğu yer döngü başlığıdır. `var` ile bildirilen döngü
değişkeni tüm döngü boyunca **tek bir bağlamadır**; `let` ile bildirilen değişken **her
turda yeniden bağlanır**.

Fark, döngü içinde üretilen fonksiyonlarda görünür:

```js
const varIsleri = [];
for (var i = 0; i < 3; i++) {
  varIsleri.push(() => i);
}
console.log(varIsleri.map((f) => f()));

const letIsleri = [];
for (let j = 0; j < 3; j++) {
  letIsleri.push(() => j);
}
console.log(letIsleri.map((f) => f()));
```

```
[ 3, 3, 3 ]
[ 0, 1, 2 ]
```

İlk döngüde üç fonksiyon da aynı `i` bağlamasını okur. Döngü bittiğinde o bağlamanın
değeri `3`'tür — koşul bu değerde yanlışa döndüğü için döngü sonlanmıştır. Fonksiyonlar
daha sonra çağrıldığında hepsi `3` görür.

İkinci döngüde her tur için ayrı bir `j` bağlaması oluşur ve her fonksiyon kendi turunun
bağlamasını okur. Bu davranış `let` bildiriminin döngü başlığına özgü bir kuralıdır:
tur başında yeni bağlama yaratılır ve önceki turun değeriyle başlatılır.

Ok fonksiyonu sözdizimi (`() => i`) burada yalnızca kısa yazım için kullanıldı;
davranışı Ok Fonksiyonları dersinde ele alınacak. Fonksiyonun kendisini yaratıldığı
kapsamdaki adlara bağlı tutmasına **kapanış (closure)** denir; Programlama Temelleri
kursunda tanımlanmıştı ve bir sonraki kursta ayrıntılanacaktır.

Koleksiyon üzerinde dönerken `const` kullanılabilir, çünkü her tur yeni bir bağlama
kurar ve tur içinde değişken yeniden atanmaz:

```js
const sicakliklar = [21.5, 19.75, 23];
for (const deger of sicakliklar) {
  console.log(deger);
}
```

```
21.5
19.75
23
```

#### Ölçüm Betiğine Uygulama

Kursun ekseni olan betik, bu kurallara göre yeniden yazıldığında hangi adın neden hangi
bildirimle verildiği açıklanabilir hale gelir:

```js
const sicakliklar = [21.5, 19.75, 23, 18.25];
const esik = 20;

let toplam = 0;
let esikUstu = 0;

for (const deger of sicakliklar) {
  toplam += deger;
  if (deger > esik) {
    const fark = deger - esik;
    esikUstu += 1;
    console.log("esik asildi, fark:", fark);
  }
}

console.log("ortalama:", toplam / sicakliklar.length);
console.log("esik ustu okuma:", esikUstu);
```

```
esik asildi, fark: 1.5
esik asildi, fark: 3
ortalama: 20.625
esik ustu okuma: 2
```

Üç karar birlikte alındı. Yeniden bağlanmayacak adlar (`sicakliklar`, `esik`, tur
değişkeni `deger`, blok içi `fark`) `const` ile bildirildi. Yalnızca birikeç görevindeki
iki ad (`toplam`, `esikUstu`) `let` aldı. `fark` adı yalnızca `if` bloğunda gerektiği
için orada bildirildi; blok kapsamı sayesinde döngünün geri kalanında görünmez.

Bu yazım kuralı kurs boyunca korunacak: **yeniden bağlanmayan her ad `const` ile, yeniden
bağlanan ad `let` ile bildirilir.** `var` yalnızca davranışı gösterilirken kullanılacak.
Gerekçe estetik değildir — bir adın yeniden bağlanıp bağlanmadığı bildirimden okunduğunda,
kodu izlemek için tüm gövdeyi taramak gerekmez.

#### Özet

- `var` fonksiyon kapsamlıdır; `let` ve `const` blok kapsamlıdır ve süslü ayraç çiftinin
  dışında görünmez.
- Aynı kapsamda `var` yeniden bildirilebilir; `let` ve `const` yeniden bildirimi
  ayrıştırma aşamasında `SyntaxError` ile reddeder.
- `const` bağlamayı sabitler, değeri değiştirilemez yapmaz: nesnenin özellikleri
  değişebilir, ada başka nesne bağlanamaz.
- `var` ile bildirilen döngü değişkeni tek bir bağlamadır; `let` her turda yeni bağlama
  kurar ve döngü içinde üretilen fonksiyonlar bu farkı gösterir.
- Kurs kuralı: yeniden bağlanmayan ad `const`, birikeç görevindeki ad `let` alır.

#### Sonraki Adım

Kapsam sorusunun yarısı yanıtlandı: bir adın nerede görüldüğü. Diğer yarısı ne zaman
erişilebilir olduğudur. Bildirimden önceki satırda aynı ada dokunmak `var` ile
`undefined` verirken `let` ile hata üretir; ikisi de bildirimin "yukarı taşındığı"
gerçeğiyle açıklanır. Sonraki ders sözcüksel kapsamı, yukarı taşımayı ve bildirim öncesi
bölgenin kurallarını ele alacak.

### Adlandırma ve Kapsam Kuralları

Source: https://academia.sh/tr/kurslar/javascript-temelleri/adlandirma-ve-kapsam-kurallari

Ad kuralları ve ayrılmış sözcükler, sözcüksel kapsamın çözümlenmesi, gölgeleme, yukarı taşıma ve zamansal ölü bölge.

Önceki ders bir adın nerede görüldüğünü belirledi. Bu ders iki soruyu daha yanıtlar:
aynı ad birden çok kapsamda bildirilmişse hangisi geçerlidir ve bir ad bildirim
satırından önce okunursa ne olur?

İki sorunun yanıtı da tek bir ilkeden çıkar: JavaScript **sözcüksel kapsam (lexical
scope)** kullanır. Bir adın hangi bağlamaya karşılık geldiği, kodun yazıldığı yere
bakılarak belirlenir; çalışma anında kimin kimi çağırdığına bakılmaz. Programlama
Temelleri kursundaki Kapsam ve Yaşam Süresi dersinde tanımlanan bu ilke, aşağıdaki
bütün davranışların kaynağıdır.

#### Ad Kuralları

Bir ad harfle, alt çizgiyle veya dolar işaretiyle başlar; devamında rakam da bulunabilir.
Adlar büyük–küçük harfe duyarlıdır: `esik` ile `Esik` iki farklı addır.

```js
const olcumSayisi = 4;
const OLCUM_ESIGI = 20;
const $ad = "A1";
const _gecici = true;
console.log(olcumSayisi, OLCUM_ESIGI, $ad, _gecici);
console.log("esik" === "Esik");
```

```
4 20 A1 true
false
```

Dilbilgisi Latin harfleri dışındaki harfleri de kabul eder, ancak kurstaki tüm adlar
ASCII karakterlerle yazılmıştır. Gerekçe pratiktir: görsel olarak birbirine benzeyen
ama farklı kod noktalarına karşılık gelen karakterler, ayırt edilmesi güç hatalar
üretir. Bilgisayarlar Nasıl Çalışır kursundaki Karakter Kodlamaları dersinde tanımlanan
kod noktası kavramı burada doğrudan işe yarar: iki ad ancak kod noktası dizileri aynıysa
aynıdır.

Dilin ayırdığı sözcükler ad olarak kullanılamaz:

```js
const class = 1;
```

```
SyntaxError: Unexpected token 'class'
```

Bu da bir ayrıştırma hatasıdır; dosya hiç çalışmaz. Ayrılmış sözcükler listesi
standartta yazılıdır ve `class`, `const`, `return`, `typeof`, `new` gibi dil yapılarını
içerir.

Adlandırma sözleşmesi olarak kursta değişken ve fonksiyon adları küçük harfle başlar ve
sonraki sözcükler büyük harfle sürer (`olcumSayisi`); yeniden hesaplanmayacak eşik ve
ayar değerleri büyük harfle yazılır (`OLCUM_ESIGI`). Sözleşme dilin kuralı değildir;
tutarlılık için seçilmiştir.

#### Sözcüksel Kapsam

Bir ad okunduğunda önce içinde bulunulan kapsama bakılır. Ad orada yoksa onu **çevreleyen
kapsama**, sonra onun çevresine bakılır; en dış kapsamda da bulunamazsa `ReferenceError`
verilir. Bu zincir, kodun yazılı yapısından çıkarılır.

```js
const esik = 20;

function disKapsam() {
  const esik = 25;
  function icKapsam() {
    return esik;
  }
  return icKapsam();
}

function cagiran() {
  const esik = 30;
  return disKapsam();
}

console.log(disKapsam());
console.log(cagiran());
console.log(esik);
```

```
25
25
20
```

İkinci satır belirleyicidir. `cagiran` fonksiyonunun içinde `esik` adı `30`'a bağlıdır ve
`disKapsam` oradan çağrılmıştır. Buna rağmen sonuç `25`'tir: `icKapsam`, adı çağıranın
kapsamında değil, **kendi yazıldığı yerin** çevreleyen kapsamında arar. Çağrı zinciri
kapsam zincirini değiştirmez.

Alternatif model — adın çağıranın kapsamında aranması — dinamik kapsam (dynamic
scoping) olarak adlandırılır ve bazı kabuk dillerinde bulunur. JavaScript bu modeli
kullanmaz. Ayrım pratik bir sonuç doğurur: bir fonksiyonun hangi adları görebildiği,
onu okuyan kişi tarafından yalnızca kaynağa bakılarak belirlenebilir.

#### Gölgeleme

İç kapsamda dış kapsamdakiyle aynı adın bildirilmesine **gölgeleme (shadowing)** denir.
Yukarıdaki örnekte `disKapsam` içindeki `esik`, en dıştaki `esik` adını gölgeler. Dış
bağlama yok olmaz; yalnızca o bölgede erişilemez hale gelir — son satırın `20` yazması
bunu gösterir.

Gölgeleme bilinçli kullanıldığında yararlıdır: dar bir bölgede aynı kavramın farklı bir
değerini taşımak, yeni bir ad uydurmaktan okunaklıdır. Kazara yapıldığında ise dış
değerin neden kullanılmadığını açıklamak güçleşir. Kural basittir: gölgeleme yapılıyorsa
bilinçli olmalı.

#### Yukarı Taşıma

Bildirimler, kaynakta yazıldıkları satırda değil, kapsamın başında işlenir. Bu davranışa
**yukarı taşıma (hoisting)** denir. Üç bildirim türü aynı biçimde taşınmaz ve fark tam
olarak burada ortaya çıkar.

`var` ile bildirilen ad, kapsamın başında yaratılır ve `undefined` ile başlatılır.
Bildirim satırına gelene kadar okunabilir; okunduğunda `undefined` verir:

```js
console.log(typeof varAdi);
var varAdi = 20;
console.log(varAdi);
```

```
undefined
20
```

`let` ve `const` ile bildirilen ad da kapsamın başında yaratılır, ama **başlatılmaz**.
Yaratılma anı ile bildirim satırı arasındaki bölgede ada dokunmak hata verir:

```js
try {
  console.log(letAdi);
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
let letAdi = 25;
console.log(letAdi);
```

```
ReferenceError: Cannot access 'letAdi' before initialization
25
```

Bu bölgeye **zamansal ölü bölge (temporal dead zone)** denir. İletiyi dikkatle okumak
gerekir: hata "ad bulunamadı" demiyor, "başlatılmadan önce erişilemez" diyor. Ad kapsamda
vardır; henüz bir değere bağlanmamıştır. Önceki dersteki `esik is not defined` iletisiyle
karşılaştırıldığında fark nettir — o ad hiç bildirilmemişti, bu ad bildirilmiş ama
başlatılmamıştır.

Ölü bölge, gölgelemeyle birleştiğinde sezgiye aykırı görünen bir sonuç üretir:

```js
const esik = 20;
function hatali() {
  try {
    console.log(esik);
    const esik = 25;
  } catch (hata) {
    console.log(hata.name + ": " + hata.message);
  }
}
hatali();
```

```
ReferenceError: Cannot access 'esik' before initialization
```

Dıştaki `esik` adı `20`'ye bağlı olmasına rağmen okunamadı. Nedeni sözcüksel kapsamın
tanımıdır: fonksiyon gövdesinde `esik` bildirildiği için, o gövdedeki her `esik`
başvurusu **yerel** bağlamayı gösterir. Yerel bağlama ise henüz başlatılmamıştır. Dış
bağlamaya erişmenin yolu yoktur; gölgeleme, bildirim satırından önce de geçerlidir.

#### Fonksiyon Bildirimleri

Fonksiyon bildirimleri, adlarıyla birlikte gövdeleriyle de yukarı taşınır. Bu yüzden
bildirim satırından önce çağrılabilirler:

```js
console.log(ortalama([21.5, 19.75, 23, 18.25]));

function ortalama(dizi) {
  let toplam = 0;
  for (const d of dizi) toplam += d;
  return toplam / dizi.length;
}
```

```
20.625
```

Fonksiyon bir ifade olarak bir değişkene atanmışsa, taşınan şey yalnızca değişken
bildirimidir; fonksiyonun kendisi atama satırında oluşur. `const` ile atanmışsa ölü
bölge kuralı geçerlidir:

```js
try {
  console.log(ortalama([1, 2]));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
const ortalama = (dizi) => dizi.reduce((t, d) => t + d, 0) / dizi.length;
console.log(ortalama([1, 2]));
```

```
ReferenceError: Cannot access 'ortalama' before initialization
1.5
```

İki tanım biçiminin davranış farkı Fonksiyon Bildirimi ve İfadesi dersinde ayrıntılı
olarak ele alınacak; burada yalnızca yukarı taşıma yönünden karşılaştırıldı.

#### Ölçüm Betiğine Uygulama

Kapsam kuralları, ölçüm betiğinin yapısını doğrudan belirler:

```js
const ESIK = 20;

function esikRaporu(sicakliklar) {
  const asanlar = [];
  for (const deger of sicakliklar) {
    if (deger > ESIK) {
      const fark = deger - ESIK;
      asanlar.push(fark);
    }
  }
  return asanlar;
}

console.log(esikRaporu([21.5, 19.75, 23, 18.25]));
console.log(typeof asanlar);
```

```
[ 1.5, 3 ]
undefined
```

`ESIK` en dış kapsamdadır ve fonksiyon içinden sözcüksel kapsam zinciriyle görülür.
`asanlar` fonksiyona, `fark` ise `if` bloğuna aittir; ikisi de dışarıdan erişilemez. Son
satırın `typeof` ile yazılması zorunluydu — doğrudan okumak `ReferenceError` verirdi.

Yapı üç kuralı birlikte uygular: her ad ihtiyaç duyulan en dar kapsamda bildirilir,
gölgeleme yapılmaz, bildirimler kullanımdan önce gelir. Üçüncüsü yukarı taşıma sayesinde
zorunlu değildir, ama ölü bölge hatalarının tamamını ortadan kaldırır.

#### Özet

- Adlar büyük–küçük harfe duyarlıdır; ayrılmış sözcükler ad olarak kullanılamaz ve bu
  ayrıştırma aşamasında yakalanır.
- JavaScript sözcüksel kapsam kullanır: bir ad, kodun yazıldığı yerin çevreleyen
  kapsamlarında aranır; çağrı zinciri bu aramayı etkilemez.
- İç kapsamda aynı adın bildirilmesi dış bağlamayı gölgeler; gölgeleme bildirim
  satırından önce de geçerlidir.
- `var` kapsam başında `undefined` ile başlatılır; `let` ve `const` başlatılmaz ve
  bildirim satırına kadar zamansal ölü bölgede kalır.
- Fonksiyon bildirimleri gövdeleriyle birlikte taşınır; bir değişkene atanmış fonksiyon
  ifadeleri taşınmaz.

#### Sonraki Adım

Dilin yapısı — kodun nasıl çalıştırıldığı, adların nasıl bildirildiği ve çözümlendiği —
kuruldu. Sıra, bu adların bağlandığı değerlerde. Sonraki konu JavaScript'in değer
modelini ele alacak: kaç ilkel tip vardır, sayı tipi hangi gösterimi kullanır ve neden
tam sayılar için bir sınır taşır. İlk ders ilkel tipleri tek tek tanıtacak.

## Değerler ve Tipler

İlkel tipler, büyük tam sayılar, tip zorlama algoritması, üç eşitlik karşılaştırması ve doğruluk değerleri.

### İlkel Tipler

Source: https://academia.sh/tr/kurslar/javascript-temelleri/ilkel-tipler

Yedi ilkel tip ve nesne ayrımı; tek sayı tipinin kayan noktalı gösterimi, özel sayı değerleri, dizgi, mantıksal değer, iki yokluk değeri ve sembol.

Önceki konu adların nasıl bildirildiğini ve çözümlendiğini kurdu. Bu konu, adların
bağlandığı değerleri ele alıyor. Programlama Temelleri kursundaki Temel Veri Tipleri
dersinde tip, bir değer kümesi ile o değerler üzerinde tanımlı işlemler olarak
tanımlanmıştı.
Bu ders o tanımı JavaScript'in değer modeline uygular.

Model küçüktür: yedi ilkel tip ve bir nesne sınıfı. Küçüklüğün bedeli, her tipin
taşıdığı kararların ağırlığıdır. Örneğin tek bir sayı tipi vardır ve tam sayılar için
ayrı bir tip yoktur; bu tek karar, ilerideki birkaç dersin konusunu belirler.

#### Yedi İlkel Tip ve Nesneler

Bir değer ya **ilkeldir** ya da bir **nesnedir**. İlkel değerlerin içeriği
değiştirilemez ve kendi başlarına özellik taşımazlar. Nesneler ise özellik kümesidir ve
içerikleri değiştirilebilir.

`typeof` işleci bir değerin tipini metin olarak verir:

```js
console.log(typeof 21.5);
console.log(typeof "A1");
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof null);
console.log(typeof 9007199254740993n);
console.log(typeof Symbol("olcum"));
console.log(typeof { istasyon: "A1" });
console.log(typeof [1, 2]);
console.log(typeof function () {});
```

```
number
string
boolean
undefined
object
bigint
symbol
object
object
function
```

Çıktı iki noktada tipe birebir karşılık gelmez. Beşinci satırda `null` bir ilkel
değerdir, ama sonuç `object`'tir; Dil Sürümleri ve Standartlaşma dersinde anlatılan
geriye dönük uyumluluğun izidir. Son satırda `function` ayrı bir tip değildir;
fonksiyonlar çağrılabilir nesnelerdir ve `typeof` bunu ayrı bir değerle bildirir.

Yedi ilkel tip şunlardır: sayı, büyük tam sayı, dizgi, mantıksal değer, `undefined`,
`null` ve sembol. Bu dersin konusu bunlardan altısıdır; büyük tam sayı bir sonraki
dersin konusudur.

#### Sayı: Tek Bir Tip

JavaScript'te tam sayı ve gerçel sayı için ayrı tipler yoktur. `number` tipinin
tamamı, Bilgisayarlar Nasıl Çalışır kursundaki Kayan Noktalı Sayılar dersinde tanımlanan
IEEE 754 çift duyarlıklı gösterimdir: 64 bit, 1 bit işaret, 11 bit kaydırmalı üs, 52 bit
saklanan mantis.

Bu, iki doğrudan sonuç doğurur.

Birincisi, tam sayı yazımı ayrı bir tip üretmez:

```js
console.log(Number.isInteger(23));
console.log(23 === 23.0);
```

```
true
true
```

`23` ile `23.0` aynı değerdir; ikisi de aynı bit örüntüsüne karşılık gelir. Bu yüzden
ilk derste `Math.max` sonucu `23` olarak yazılmıştı — kesir kısmı sıfır olan bir kayan
noktalı sayı, metne çevrilirken kesir gösterilmeden yazılır.

İkincisi, ikilik kesir olarak yazılamayan onluk değerler yaklaşık tutulur:

```js
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
```

```
0.30000000000000004
false
```

Bu sonuç bir sürpriz değil, gösterimin doğrudan sonucudur. Kayan Noktalı Sayılar
dersindeki model burada birebir geçerlidir: $0{,}1$ ve $0{,}2$ değerleri ikilik kesir
olarak sonlu yazılamaz, en yakın temsil edilebilir değere yuvarlanır ve toplama bu
yuvarlanmış değerler üzerinde yapılır. Sonuç, $0{,}3$'ün temsiline değil, ona en yakın
başka bir değere düşer.

Aynı derste kurulan pratik kural burada da geçerlidir: kayan noktalı değerler eşitlikle
değil, tolerans ile karşılaştırılır. Para gibi kesinlik gerektiren büyüklükler ise en
küçük birimi taşıyan tam sayılarla tutulur.

Tam sayıların güvenli aralığı da mantis genişliğinden gelir. Mantis 53 bit duyarlık
sağladığı için, mutlak değeri $2^{53}-1$ değerini aşan tam sayılar birbirinden ayırt
edilemez:

```js
console.log(Number.MAX_SAFE_INTEGER);
console.log(9007199254740992 === 9007199254740993);
```

```
9007199254740991
true
```

İki farklı tam sayının eşit çıkması, ikisinin de aynı temsil edilebilir değere
yuvarlanmasındandır. Bu sınırın aşıldığı durumlar bir sonraki dersin konusudur.

#### Özel Sayı Değerleri

Gösterim üç özel değer taşır ve üçü de dilde doğrudan görünür:

```js
console.log(1 / 0, -1 / 0, 0 / 0);
console.log(Object.is(0, -0), 1 / -0);
```

```
Infinity -Infinity NaN
false -Infinity
```

Sıfıra bölme hata vermez; sonsuzluk değeri üretir. Tanımsız işlemler `NaN` (not a
number) verir. `NaN` bir sayı **değeri**dir ve tipi `number`'dır; adı, temsil edilen
şeyin bir sayı olmadığını söyler.

İşaretli sıfır da gösterimin parçasıdır: `0` ile `-0` farklı bit örüntüleridir ama
eşitlik karşılaştırmalarında eşit sayılırlar. Ayrımı gören işlem `Object.is`'tir. `-0`
değeri çoğu hesapta görünmez; yalnızca sıfıra bölmede işareti belirlediği için önem
kazanır.

#### Dizgi

Dizgi tipi, metin taşır. Dizgiler tek tırnak, çift tırnak veya ters tırnak ile yazılır;
ilk ikisi arasında davranış farkı yoktur. Ters tırnaklı biçim ayrı yeteneklere sahiptir
ve Dizgiler dersinde ele alınacaktır.

```js
const ad = "ölçüm";
console.log(ad.length);
console.log(ad[0]);
console.log(ad.toUpperCase());
console.log("A1".codePointAt(0));
```

```
5
ö
ÖLÇÜM
65
```

Dizgi uzunluğu karakter sayısı değil, **16 bitlik kod birimi** sayısıdır. Bilgisayarlar
Nasıl Çalışır kursundaki Karakter Kodlamaları dersinde tanımlanan vekil çift kavramı
burada görünür hale gelir: temel çok dilli düzlemin dışındaki bir kod noktası iki kod
birimiyle temsil edilir ve `length` bunu iki sayar.

```js
console.log("😀".length);
```

```
2
```

Bu, dizgi tipini anlamanın anahtarıdır: dizgi bir kod noktası dizisi değil, kod birimi
dizisidir. Ayrım, karakter sayma ve dilimleme işlemlerinde sonuç değiştirir.

#### Mantıksal Değer

Mantıksal tipin iki değeri vardır: `true` ve `false`. Karşılaştırmalardan üretilirler ve
koşullu yapıların girdisidir. JavaScript, mantıksal olmayan değerleri de koşulda
kullanılabilir sayar; hangi değerin doğru, hangisinin yanlış sayıldığı Doğruluk Değerleri
dersinin konusudur.

#### İki Yokluk Değeri

Çoğu dilde tek bir yokluk değeri bulunur. JavaScript'te iki tane vardır ve anlamları
ayrıdır.

`undefined`, **bir değerin hiç verilmemiş olmasıdır**. Bildirilmiş ama atanmamış bir
değişken, var olmayan bir nesne özelliği ve karşılığı verilmemiş bir parametre bu değeri
taşır. Dil onu kendiliğinden üretir.

`null`, **bilinçli olarak yerleştirilmiş bir yokluktur**. Dil onu kendiliğinden üretmez;
program yazarı "burada değer yok" demek için atar.

```js
const kayit = { istasyon: "A1", sicaklik: 21.5 };
console.log(kayit.nem);
console.log("nem" in kayit);

let sonrakiOkuma;
console.log(sonrakiOkuma);

const kalibrasyon = null;
console.log(kalibrasyon);
```

```
undefined
false
undefined
null
```

Ayrım ölçüm verilerinde doğrudan işe yarar. Bir kayıtta nem alanı **hiç yoksa** okuma
`undefined` verir; alan var ama sensör değer üretmemişse `null` yazmak, "ölçülmedi"
bilgisini korur. İkisi aynı şey değildir: birincisi verinin biçimiyle, ikincisi verinin
içeriğiyle ilgilidir.

#### Sembol

Sembol, her çağrıda benzersiz bir değer üreten ilkel tiptir. Aynı açıklama metniyle
üretilen iki sembol birbirine eşit değildir:

```js
console.log(Symbol("kaynak") === Symbol("kaynak"));
```

```
false
```

Kullanım alanı, nesnelere çakışma riski olmadan özellik eklemektir. Sembol anahtarlı
özellikler sıradan anahtar listelerinde ve dizileştirmede görünmez:

```js
const kaynak = Symbol("kaynak");
const kayit = { istasyon: "A1", sicaklik: 21.5, [kaynak]: "yerel dosya" };
console.log(Object.keys(kayit));
console.log(JSON.stringify(kayit));
console.log(kayit[kaynak]);
```

```
[ 'istasyon', 'sicaklik' ]
{"istasyon":"A1","sicaklik":21.5}
yerel dosya
```

Ölçüm kaydına eklenen köken bilgisi, kaydın veri içeriğini kirletmeden taşınmış oldu.
Sembollerin dil içindeki ikinci görevi — nesnelerin belirli davranışlarını
uyarlamak — bir sonraki kursun konusudur.

#### İlkel Değerler Değiştirilemez

İlkel bir değerin içeriği değiştirilemez. Dizgi üzerinde konum atama girişimi katı kipte
hata verir:

```js
const ad = "A1";
try {
  ad[0] = "B";
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
console.log(ad);
```

```
TypeError: Cannot assign to read only property '0' of string 'A1'
A1
```

Aynı işlem katı kipte olmayan bir betikte hata vermez; sessizce yok sayılır ve dizgi yine
değişmez. Kod Çalıştırma Yolları dersinde anlatılan katı kip farkının ikinci örneğidir
bu: davranış aynı, geri bildirim farklıdır.

Bu nedenle dizgi yöntemleri asla özgün değeri değiştirmez, her zaman yeni bir dizgi
üretir. Programlama Temelleri kursundaki değiştirilemez–değiştirilebilir ayrımı burada
tiplere göre kesindir: yedi ilkel tipin tamamı değiştirilemez, nesneler
değiştirilebilirdir.

#### Ölçüm Kaydının Tipleri

Kursun ekseni olan veri, bu tiplerin bir araya gelmesidir:

```js
const kayit = {
  istasyon: "A1",
  sicaklik: 21.5,
  nem: null,
  dogrulandi: false,
};

for (const anahtar of Object.keys(kayit)) {
  console.log(anahtar, "->", typeof kayit[anahtar]);
}
console.log("eksik alan:", typeof kayit.basinc);
```

```
istasyon -> string
sicaklik -> number
nem -> object
dogrulandi -> boolean
eksik alan: undefined
```

Üçüncü satır bir hatırlatmadır: `nem` alanı `null` taşıdığı için `typeof` sonucu
`object` çıkar. Bir alanın gerçekten boş olup olmadığı `typeof` ile değil, doğrudan
karşılaştırmayla sınanır. Bu karşılaştırmaların hangi kurala göre yapıldığı Eşitlik
Karşılaştırmaları dersinin konusudur.

#### Özet

- Değerler ilkel ya da nesnedir; yedi ilkel tip vardır ve hepsi değiştirilemezdir.
- `typeof null` sonucu `object`, fonksiyonların sonucu `function`'dır; ikisi de tip
  listesine birebir karşılık gelmez.
- Tek sayı tipi IEEE 754 çift duyarlıklı gösterimdir: `23` ile `23.0` aynı değerdir,
  onluk kesirler yaklaşıktır ve güvenli tam sayı aralığı $2^{53}-1$ ile sınırlıdır.
- `Infinity`, `-Infinity`, `NaN` ve `-0` gösterimin parçasıdır; `NaN` tipi `number`'dır.
- Dizgi uzunluğu kod birimi sayısıdır, karakter sayısı değil.
- `undefined` verilmemiş bir değeri, `null` bilinçli yerleştirilmiş bir yokluğu belirtir.

#### Sonraki Adım

Güvenli tam sayı aralığının üstünde iki farklı tam sayının eşit çıkması, bu dersin
çözümsüz bıraktığı tek noktadır. Sonraki ders bu sınırın aşıldığı durumları ve dilin
buna verdiği yanıtı ele alacak: keyfi büyüklükte tam sayı taşıyan ayrı bir ilkel tip,
onun işleçlerdeki davranışı ve sayı tipiyle karıştırıldığında ortaya çıkan hata.

### Büyük Tam Sayılar

Source: https://academia.sh/tr/kurslar/javascript-temelleri/buyuk-tam-sayilar

Güvenli tam sayı aralığının aşıldığı durumlar, keyfi büyüklükte tam sayı tipi, işleç davranışı, tip karıştırma yasağı ve dizileştirme sınırı.

Önceki ders, sayı tipinin tamamının IEEE 754 çift duyarlıklı gösterim olduğunu ve bu
gösterimin tam sayılar için $2^{53}-1$ değerinde bir sınır taşıdığını gösterdi. Sınırın
üstünde iki farklı tam sayı aynı bit örüntüsüne yuvarlanır ve eşit çıkar.

Bu ders o sınırın gerçekten aşıldığı durumları ve dilin buna verdiği yanıtı ele alır:
keyfi büyüklükte tam sayı taşıyan ayrı bir ilkel tip. Tipin kendisi kadar, sayı tipiyle
arasına konan kesin sınır da önemlidir.

#### Sınır Nerede Aşılır

Güvenli aralık yaklaşık dokuz katrilyondur. Günlük hesaplarda uzak görünen bu değer,
belirli veri türlerinde tek bir okumayla geçilir:

- **Nanosaniye çözünürlüklü zaman damgaları.** Bir başlangıç anından itibaren geçen
  nanosaniye sayısı, milisaniyeye göre bir milyon kat büyüktür ve aralığın dışına çıkar.
- **Başka sistemlerden gelen 64 bitlik tam sayı kimlikleri.** Kimlik üzerinde aritmetik
  yapılmasa bile, değeri kaybetmeden okuyup geri yazmak gerekir.
- **Uzun süre biriken sayaçlar.** Yüksek örnekleme hızlı bir ölçüm ağında toplam örnek
  sayısı bu aralığa ulaşabilir.

Üçünde de ortak nokta, hassasiyetin son basamağa kadar korunması gerektiğidir. Sayı
tipiyle çalışıldığında kayıp sessizdir; hata verilmez, yalnızca yanlış değer taşınır.

#### Ayrı Bir İlkel Tip

Büyük tam sayı (BigInt), keyfi basamak sayısında tam sayı taşıyan ilkel tiptir. İki
biçimde yazılır: sayı yazımının sonuna `n` eklenerek veya bir dönüştürme çağrısıyla.

```js
const a = 9007199254740993n;
const b = BigInt("9007199254740993");
console.log(a === b);
console.log(typeof a);
console.log(a + 1n);
```

```
true
bigint
9007199254740994n
```

Önceki derste bu değer sayı tipiyle temsil edilemiyordu; burada tam olarak korunuyor.
Çıktıdaki `n` soneki değerin parçası değildir, çalışma zamanının inceleme biçimidir.
Değer metne çevrildiğinde sonek görünmez:

```js
console.log(String(10n));
console.log(10n.toString());
console.log(10n);
```

```
10
10
10n
```

İlk iki satır dilin metne çevirme kuralının sonucudur ve her ortamda aynıdır. Üçüncü
satırdaki gösterim ortamın inceleme biçimidir. Kod Çalıştırma Yolları dersinde kurulan
ayrım burada tekrar karşımıza çıkıyor.

#### İşleç Davranışı

Aritmetik işleçler tam sayı anlambilimiyle çalışır. En belirgin fark bölmededir:

```js
console.log(7n / 2n);
console.log(7n % 2n);
console.log(2n ** 64n);
```

```
3n
1n
18446744073709551616n
```

Bölme kesir üretmez; sonuç sıfıra doğru kırpılır. Programlama Temelleri kursundaki Temel
Veri Tipleri dersinde iki bölme işleminin ayrımı kurulmuştu — burada büyük tam sayı
tipinde yalnızca tam bölme vardır. Kırpma yönü sıfıra doğrudur, aşağı doğru değil;
negatif işlenenlerde bu ayrım sonuç değiştirir.

Üstel işlem, sayı tipinde temsil edilemeyecek büyüklükleri tam olarak verir. $2^{64}$
değeri yirmi basamaklıdır ve son basamağına kadar doğrudur.

#### Tipler Karıştırılamaz

İki sayı tipi aritmetik işleçlerde bir araya gelemez:

```js
try {
  console.log(1n + 1);
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError: Cannot mix BigInt and other types, use explicit conversions
```

Bu yasak bir eksiklik değil, bilinçli bir tasarım kararıdır. İki tipin anlambilimi
uyuşmaz: sayı tipinde bölme kesir üretir, büyük tam sayıda üretmez; sayı tipinde büyük
değerler yuvarlanır, büyük tam sayıda yuvarlanmaz. Karışık bir ifadenin hangi kuralla
değerlendirileceği tek biçimde seçilemez. Dil, seçimi program yazarına bırakır ve açık
dönüşüm ister.

Karşılaştırma işleçleri farklı davranır. Değer karşılaştırması yapan işleçler iki tipi
birlikte ele alabilir; tip de karşılaştıran işleç ayırır:

```js
console.log(1n == 1);
console.log(1n === 1);
console.log(2n > 1);
```

```
true
false
true
```

İlk ve üçüncü satırlar matematiksel değeri karşılaştırır. İkinci satır ise tipleri de
karşılaştırdığı için yanlış verir. Bu üç işlecin kuralları Eşitlik Karşılaştırmaları
dersinde adım adım tanımlanacak.

#### Dönüşümün Bedeli

İki tip arasında dönüşüm açıkça yazılabilir, ama her yön güvenli değildir.

Büyük tam sayıdan sayıya dönüşüm, aralık dışındaki değerlerde duyarlık kaybeder:

```js
const zamanNs = 1700000000123456789n;
console.log(Number(zamanNs));
console.log(BigInt(Number(zamanNs)));
```

```
1700000000123456800
1700000000123456768n
```

İki satır birlikte okunmalıdır. Birinci satır, dönüşümden çıkan sayının metne çevrilmiş
halidir; metne çevirme, aynı bit örüntüsüne geri dönen en kısa onluk gösterimi seçer, bu
yüzden yuvarlak görünür. İkinci satır aynı değeri kayıpsız bir tiple yazar ve gerçek
değeri gösterir: `...768`. Yani hem özgün değer kaybolmuştur hem de kaybın miktarı ilk
bakışta göründüğünden farklıdır.

Sayıdan büyük tam sayıya dönüşüm ise kesirli değerleri reddeder:

```js
try {
  console.log(BigInt(1.5));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
RangeError: The number 1.5 cannot be converted to a BigInt because it is not an integer
```

Sayı tipiyle çalışan yerleşik yöntemler de büyük tam sayı kabul etmez:

```js
try {
  console.log(Math.max(1n, 2n));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError: Cannot convert a BigInt value to a number
```

`Math` nesnesinin tamamı kayan noktalı sayılar için tanımlıdır. Büyük tam sayılarla
karşılaştırma yapmak gerekiyorsa karşılaştırma işleçleri kullanılır.

#### Ölçüm Betiğine Uygulama

Ölçüm kayıtlarına nanosaniye çözünürlüklü zaman damgası eklendiğinde, iki tip aynı
kayıtta yan yana bulunur:

```js
const okumalar = [
  { istasyon: "A1", zamanNs: 1700000000123456789n, sicaklik: 21.5 },
  { istasyon: "A1", zamanNs: 1700000000923456789n, sicaklik: 21.7 },
];

const fark = okumalar[1].zamanNs - okumalar[0].zamanNs;
console.log("fark (ns):", fark.toString());
console.log("fark (ms):", (fark / 1000000n).toString());
```

```
fark (ns): 800000000
fark (ms): 800
```

Zaman farkı nanosaniye duyarlığında hesaplandı, sonra milisaniyeye çevrildi. Çevrimde
kesir atıldı — tam bölme kırpma yapar. Kesirli bir sonuç gerekiyorsa, bölme sayı tipine
geçilerek yapılır ve bu noktada duyarlık kaybı bilinçli olarak kabul edilir.

Sıcaklık alanı sayı tipinde kaldı; kayan noktalı bir büyüklük için doğru seçim odur.
İki tipin aynı kayıtta bulunması sorun değildir — sorun, ikisini aynı ifadede
karıştırmaktır.

#### Dizileştirme Sınırı

Büyük tam sayılar, verinin JSON biçiminde yazılmasını engeller:

```js
try {
  console.log(JSON.stringify({ sayac: 10n }));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
console.log(JSON.stringify({ sayac: String(10n) }));
```

```
TypeError: Do not know how to serialize a BigInt
{"sayac":"10"}
```

Neden bir eksiklik değil, biçimin tanımıdır: JSON'un sayı gösteriminin duyarlık garantisi
yoktur ve okuyan taraf değeri sayı tipine çevirebilir. Değeri kayıpsız aktarmak için metin
olarak yazmak, biçimin sınırını kabul eden çözümdür. JSON'un sınırları ayrı bir dersin
konusudur.

#### Özet

- Güvenli tam sayı aralığı nanosaniye zaman damgaları, 64 bitlik dış kimlikler ve uzun
  süre biriken sayaçlarda aşılır; kayıp sessizdir.
- Büyük tam sayı ayrı bir ilkel tiptir; `n` sonekiyle veya açık dönüşümle yazılır ve
  keyfi basamak sayısı taşır.
- Bölme kesir üretmez, sıfıra doğru kırpar; `Math` yöntemleri bu tiple çalışmaz.
- Aritmetik işleçlerde iki sayı tipi karıştırılamaz; karşılaştırma işleçleri değer
  karşılaştırmasında birlikte çalışır, tip karşılaştıran işleç ayırır.
- Büyük tam sayıdan sayıya dönüşüm aralık dışında duyarlık kaybettirir; JSON'a doğrudan
  yazılamaz, metin olarak aktarılır.

#### Sonraki Adım

Bu ders, iki tip arasındaki dönüşümün açıkça yazılmasını gerektiren tek durumu gösterdi.
Dilin geri kalanında durum tersidir: tipler çoğu işleçte kendiliğinden dönüştürülür ve
sonuç, dönüşümün hangi sırayla yapıldığına bağlıdır. Sonraki ders bu örtük zorlamanın
adımlarını tanımlayacak; `[] + {}` gibi ifadelerin sonucu ezber değil, izlenebilir bir
yordamın çıktısı olarak okunacak.

### Tip Dönüşümü ve Zorlama

Source: https://academia.sh/tr/kurslar/javascript-temelleri/tip-donusumu-ve-zorlama

Açık dönüşüm ile örtük zorlamanın ayrımı; sayıya, metne ve ilkel değere çevirme yordamları; toplama işlecinin iki yüzü ve metinden veri okuma.

Önceki ders, iki sayı tipinin aritmetik işleçlerde karıştırılmasının reddedildiğini
gösterdi. Bu, dilin genel kuralı değildir; istisnadır. Dilin geri kalanında tipler
kendiliğinden dönüştürülür ve sonuç, dönüşümün hangi sırayla yapıldığına bağlıdır.

Programlama Temelleri kursundaki Tip Dönüşümü dersinde açık dönüşüm ile örtük dönüşüm
ayrımı kurulmuştu. Bu ders o ayrımı JavaScript'e uygular ve örtük dönüşümün — bu kursta
**zorlama (coercion)** adıyla anılacak olan davranışın — adımlarını tanımlar. Amaç
sonuçları ezberlemek değil, sonucu üreten yordamı izleyebilmektir.

#### Üç Hedef Tip

Zorlama her zaman üç hedeften birine doğrudur: sayı, dizgi veya mantıksal değer.
Standart bunlar için üç soyut işlem tanımlar. Bu derste ilk ikisi ele alınacak;
mantıksal değere çevirme Doğruluk Değerleri dersinin konusudur.

Sayıya çevirme yordamı, her ilkel tip için sabit bir kural verir:

```js
console.log(Number("21.5"));
console.log(Number(""));
console.log(Number("   "));
console.log(Number("21.5 C"));
console.log(Number(null));
console.log(Number(undefined));
console.log(Number(true), Number(false));
```

```
21.5
0
0
NaN
0
NaN
1 0
```

Dört satır dikkat ister. Boş dizgi ve yalnızca boşluk içeren dizgi sıfır verir — metnin
"sayı içermemesi", sıfır sayılmasıyla sonuçlanır. Sayıyla başlayıp harfle süren metin
ise `NaN` verir; yordam metnin **tamamının** geçerli bir sayı yazımı olmasını ister.
`null` sıfıra, `undefined` ise `NaN`'a çevrilir; iki yokluk değerinin ayrımı burada da
sürüyor.

Metne çevirme yordamı da tip başına tanımlıdır:

```js
console.log(String(21.5));
console.log(String(null), String(undefined), String(true));
console.log(String([1, 2, 3]));
console.log(String([]));
console.log(String([null, undefined, 3]));
console.log(String({ a: 1 }));
```

```
21.5
null undefined true
1,2,3

,,3
[object Object]
```

Diziler elemanlarını virgülle birleştirir; boş dizi boş metin verir. Dizi içindeki
`null` ve `undefined` değerleri boş metne çevrilir — dördüncü satırdaki iki baştaki
virgül bundandır. Sıradan nesneler ise içeriklerini göstermez; `[object Object]` sabit
metnini verir.

#### Nesneden İlkel Değere

Yukarıdaki dizi ve nesne sonuçları ara bir adımdan geçer. Bir nesne sayı veya metin
bekleyen bir yere girdiğinde, önce **ilkel değere çevrilir**. Bu çevrim, nesnenin iki
yöntemini belirli bir sırayla dener: sayısal bağlamda önce değer üreten yöntem, metinsel
bağlamda önce metin üreten yöntem çağrılır.

Davranış doğrudan gözlemlenebilir:

```js
const nesne = {
  valueOf() { return 42; },
  toString() { return "kirk iki"; },
};
console.log(nesne * 2);
console.log(String(nesne));
console.log(nesne + 1);
```

```
84
kirk iki
43
```

Çarpma sayısal bağlamdır: değer üreten yöntem çağrıldı. Metne çevirme metinsel
bağlamdır: metin üreten yöntem çağrıldı. Üçüncü satır ise toplama işlecinin özel
durumudur; bir sonraki bölümün konusudur.

Sıradan bir nesnede değer üreten yöntem nesnenin kendisini döndürür — yani ilkel bir
değer vermez. Bu yüzden sıra metin üreten yönteme geçer ve `[object Object]` çıkar.
Diziler metin üreten yöntemi elemanları birleştirecek biçimde tanımladığı için `1,2,3`
verir.

#### Toplama İşlecinin İki Yüzü

Toplama işleci diğer aritmetik işleçlerden ayrılır. Adımları şöyledir: her iki işlenen
ilkel değere çevrilir; **ikisinden biri dizgi ise** sonuç metin birleştirmesidir, değilse
her ikisi sayıya çevrilir ve toplanır.

```js
console.log(1 + 2);
console.log("1" + 2);
console.log(1 + 2 + "3");
console.log("1" + 2 + 3);
```

```
3
12
33
123
```

Son iki satır, işlecin soldan sağa birleştiğini gösterir. Üçüncü satırda `1 + 2` önce
sayısal olarak hesaplanır, sonra `3` sonucu metne çevrilip birleştirilir. Dördüncü
satırda ilk işlem zaten birleştirmedir; sonuç dizgi olduğu için ikinci işlem de
birleştirme olur. İşlem sırası, sonucun tipini belirliyor.

Diğer aritmetik işleçlerde bu ikilik yoktur; her zaman sayıya çevirirler:

```js
console.log("21.5" - 1);
console.log("21.5" * 2);
console.log(1 + null);
console.log(1 + undefined);
console.log(true + true);
```

```
20.5
43
1
NaN
2
```

Üçüncü ve dördüncü satırlar sayıya çevirme kurallarının doğrudan sonucudur: `null` sıfır,
`undefined` ise `NaN` verir ve `NaN` içeren her aritmetik işlem `NaN` üretir.

Sık alıntılanan ifadeler de aynı iki adımdan çıkar:

```js
console.log([] + {});
console.log([] + []);
```

```
[object Object]

```

Birinci satır: boş dizi ilkel değere çevrilince boş metin, nesne çevrilince
`[object Object]` olur; biri dizgi olduğu için birleştirme yapılır. İkinci satır: iki boş
metin birleşir ve boş satır yazılır. Ortada gizem yoktur — yalnızca iki adımlı bir
yordam vardır.

#### Açık Dönüşüm Araçları

Zorlamaya güvenmek yerine dönüşümü yazmak, kodun okunmasını kolaylaştırır. Dört araç
ayırt edilmelidir:

```js
console.log(parseInt("21.5 C"), parseFloat("21.5 C"));
console.log(parseInt("  42px"), Number("  42px"));
console.log(parseInt(""), Number(""));
console.log(parseInt("1f", 16));
console.log(+"21.5", +"", +"abc");
```

```
21 21.5
42 NaN
NaN 0
31
21.5 0 NaN
```

`Number`, metnin tamamının geçerli olmasını ister; geçersizse `NaN` verir, boş metni
sıfır sayar. `parseInt` ve `parseFloat` ise **baştan başlayıp gidebildiği kadar okur**;
geri kalanı yok sayar, hiç okuyamazsa `NaN` verir. `parseInt` ayrıca bir taban parametresi
alır; Bilgisayarlar Nasıl Çalışır kursundaki Onaltılık ve Sekizlik Tabanlar dersindeki
gösterimler bu parametreyle okunur.

Tek terimli artı işleci `Number` ile aynı kuralı uygular; kısalığı okunaklılığın önüne
geçtiği için kursta kullanılmayacaktır.

İki aracın seçimi veriye bağlıdır. Alan "yalnızca sayı içermeli" ise `Number` doğru
seçimdir: bozuk veriyi `NaN` ile bildirir. Alan "sayıyla başlayan bir metin" ise
`parseFloat` doğru seçimdir. Yanlış seçim, bozuk verinin sessizce geçmesine yol açar.

#### Ölçüm Betiğine Uygulama

Ölçüm kayıtları çoğunlukla metin olarak gelir. Aşağıdaki betik, noktalı virgülle ayrılmış
satırları okuyup sayısal alanları çevirir:

```js
const satirlar = ["A1;21.5;48", "A2;19.75;55", "B1;;41", "B2;abc;60"];

for (const satir of satirlar) {
  const [istasyon, sicaklikMetni, nemMetni] = satir.split(";");
  const sicaklik = Number(sicaklikMetni);
  const nem = Number(nemMetni);
  console.log(istasyon, sicaklik, nem, Number.isNaN(sicaklik));
}
```

```
A1 21.5 48 false
A2 19.75 55 false
B1 0 41 false
B2 NaN 60 true
```

Son iki satır, verinin iki farklı bozukluğunu gösteriyor ve yalnızca biri yakalanıyor.

`B2` kaydında sıcaklık alanı `abc` yazıyor; `Number` bunu `NaN` yaptı ve `Number.isNaN`
denetimi durumu bildirdi. `B1` kaydında ise sıcaklık alanı **boştur** ve `Number` boş
metni sıfıra çevirdi. Sıfır geçerli bir sıcaklık değeridir; denetim bu kaydı sağlam
sandı. Eksik ölçüm, sessizce sıfır dereceye dönüştü.

Doğru davranış, dönüşümden önce alanın boş olup olmadığını sınamaktır:

```js
function sicaklikOku(metin) {
  if (metin.trim() === "") {
    return null;
  }
  const deger = Number(metin);
  return Number.isNaN(deger) ? null : deger;
}

for (const alan of ["21.5", "", "   ", "abc", "0"]) {
  console.log(JSON.stringify(alan), "->", sicaklikOku(alan));
}
```

```
"21.5" -> 21.5
"" -> null
"   " -> null
"abc" -> null
"0" -> 0
```

Boş alan ile bozuk alan artık ayrı ayrı yakalanıyor ve geçerli bir sıfır ölçümü
korunuyor. Son satır bu yüzden önemlidir: sıfır, eksik veri değildir.

Aynı işi daha kısa yazma girişimi tam bu noktada kırılır:

```js
function sicaklikOkuKisa(metin) {
  const deger = Number(metin);
  return deger ? deger : null;
}

for (const alan of ["21.5", "", "abc", "0"]) {
  console.log(JSON.stringify(alan), "->", sicaklikOkuKisa(alan));
}
```

```
"21.5" -> 21.5
"" -> null
"abc" -> null
"0" -> null
```

Üç satır doğru, sonuncusu yanlıştır: geçerli bir sıfır ölçümü eksik veriye dönüştü.
Nedeni dönüşüm değil, koşulun `0` değerini nasıl değerlendirdiğidir. Bu davranışın kuralı
Doğruluk Değerleri dersinde tanımlanacak.

Eksik değer için `null` seçilmesi bilinçlidir: İlkel Tipler dersindeki ayrım gereği
`null`, "ölçülmedi" bilgisini taşır; `undefined` ise alanın hiç bulunmadığını söylerdi.

#### Özet

- Zorlama üç hedefe doğrudur: sayı, dizgi ve mantıksal değer. Her hedef için standartta
  adım adım tanımlı bir yordam vardır.
- Sayıya çevirmede boş dizgi sıfır, geçersiz metin `NaN`, `null` sıfır, `undefined`
  `NaN` verir.
- Nesneler önce ilkel değere çevrilir; sayısal bağlamda değer üreten, metinsel bağlamda
  metin üreten yöntem önceliklidir.
- Toplama işleci, işlenenlerden biri ilkel değere çevrildikten sonra dizgi ise birleştirme
  yapar; diğer aritmetik işleçler her zaman sayıya çevirir.
- `Number` metnin tamamını ister, `parseInt`/`parseFloat` gidebildiği kadar okur; alanın
  anlamına göre seçilirler.
- Boş metnin sıfıra çevrilmesi, eksik ölçümü geçerli bir değere dönüştürür; dönüşümden
  önce boşluk denetimi gerekir.

#### Sonraki Adım

Bu derste `sicaklik === null` benzeri karşılaştırmalar kullanıldı ama kuralları
tanımlanmadı. Sonraki ders üç karşılaştırma işlecini adım adım ele alacak: tip
karşılaştıran katı eşitlik, zorlama uygulayan gevşek eşitlik ve `NaN` ile `-0` durumlarını
ayıran değer eşitliği. Gevşek eşitliğin adımları, bu dersteki zorlama yordamlarının
doğrudan devamıdır.

### Eşitlik Karşılaştırmaları

Source: https://academia.sh/tr/kurslar/javascript-temelleri/esitlik-karsilastirmalari

Katı eşitlik, gevşek eşitliğin adım adım algoritması, değer eşitliği ve nesnelerde kimlik karşılaştırması.

Önceki ders zorlama yordamlarını tanımladı: bir değerin sayıya, metne ve ilkel değere
nasıl çevrildiğini. Bu ders o yordamların en çok kullanıldığı yeri ele alır. JavaScript'te
iki değeri karşılaştırmanın üç ayrı yolu vardır ve üçü farklı sorulara yanıt verir.

Üçü de tek bir eksende ayrışır: karşılaştırmadan önce zorlama uygulanıyor mu ve özel
sayı değerleri (`NaN`, `-0`) nasıl ele alınıyor. Ayrımı bilmek, "neden eşit çıktı"
sorusunu tahminden çıkarıp adım takibine dönüştürür.

#### Katı Eşitlik

`===` işleci **önce tipe bakar**. Tipler farklıysa sonuç doğrudan yanlıştır; zorlama
yapılmaz. Tipler aynıysa değerler karşılaştırılır.

```js
console.log(21.5 === 21.5);
console.log("A1" === "A1");
console.log(1 === "1");
console.log(NaN === NaN);
console.log(0 === -0);
```

```
true
true
false
false
true
```

Üçüncü satır tip farkından yanlış verdi. Son iki satır ise sayı tipine özgü iki
istisnadır ve ikisi de IEEE 754 gösteriminden gelir. `NaN`, kendisi dahil hiçbir değere
eşit değildir; standart bunu açıkça yazar. İşaretli sıfırlar ise farklı bit örüntüleri
olmasına rağmen eşit sayılır.

`NaN`'ın kendine eşit olmaması, bir değerin `NaN` olup olmadığını eşitlikle sınamayı
imkânsız kılar. Bunun için ayrı bir yoklama vardır:

```js
console.log(Number.isNaN(NaN));
console.log(Number.isNaN("abc"));
console.log(isNaN("abc"));
```

```
true
false
true
```

İki yoklamanın farkı önemlidir. `Number.isNaN`, değerin **sayı tipinde ve `NaN`** olmasını
ister. Genel yoklama ise argümanı önce sayıya çevirir; `"abc"` metni `NaN`'a çevrildiği
için doğru verir. İkincisi "bu değer sayıya çevrilemiyor" sorusunu yanıtlar, birincisi
"bu değer `NaN` mı" sorusunu. Kursta yalnızca birincisi kullanılacaktır.

#### Gevşek Eşitlik

`==` işleci, tipler farklıysa zorlama uygulayarak ortak bir tipe iner. Adımları
standartta sırayla tanımlıdır ve pratikte şu beş kural yeterlidir:

1. Tipler aynıysa katı eşitlik uygulanır.
2. Bir yan `null`, diğeri `undefined` ise sonuç doğrudur. Bu ikisi başka hiçbir değere
   gevşek eşit değildir.
3. Bir yan sayı, diğeri dizgi ise dizgi sayıya çevrilir.
4. Bir yan mantıksal değer ise **önce o yan sayıya çevrilir**, sonra kurallar yeniden
   uygulanır.
5. Bir yan nesne, diğeri ilkel ise nesne ilkel değere çevrilir ve kurallar yeniden
   uygulanır.

Kuralların sonuçları:

```js
console.log(null == undefined);
console.log(null == 0, undefined == 0);
console.log("1" == 1);
console.log("" == 0);
console.log("0" == 0);
console.log("" == "0");
```

```
true
false false
true
true
true
false
```

İkinci satır ikinci kuralın doğrudan sonucudur: `null`, sayıya çevrilmez; yalnızca
`undefined` ile eşleşir. Bu, `null` denetimini gevşek eşitlikle yazmayı güvenli kılan tek
davranıştır.

Mantıksal değer kuralı sezgiye en aykırı sonuçları üretir:

```js
console.log(true == 1, true == "1", false == "");
console.log([] == false);
console.log([21.5] == 21.5);
console.log([1, 2] == "1,2");
```

```
true true true
true
true
true
```

İkinci satırı adım adım okumak yöntemi gösterir. `[] == false` ifadesinde önce dördüncü
kural uygulanır: `false` sayıya çevrilir, `0` olur. Şimdi bir yan nesne, diğeri sayıdır;
beşinci kural gereği dizi ilkel değere çevrilir ve boş metin olur. Son adımda üçüncü
kural boş metni sayıya çevirir; sonuç `0 == 0` olur ve doğru çıkar. Hiçbir adım keyfi
değildir.

#### Gevşek Eşitlik Geçişli Değildir

Bir eşitlik bağıntısından beklenen özelliklerden biri geçişliliktir: $a = b$ ve $b = c$
ise $a = c$ olmalıdır. Gevşek eşitlik bu özelliği taşımaz:

```js
console.log("0" == 0);
console.log(0 == "");
console.log("0" == "");
```

```
true
true
false
```

İlk iki satırda karşılaştırma sayı üzerinden yapıldı; üçüncü satırda iki yan da dizgi
olduğu için birinci kural devreye girdi ve zorlama olmadı. Sonuç, birbiriyle çelişmeyen
ama geçişli olmayan bir bağıntıdır.

Buradan kursun kuralı çıkar:

> **Eşitlik karşılaştırmaları `===` ile yazılır.** Tek istisna, bir değerin `null` veya
> `undefined` olup olmadığını birlikte sınayan `deger == null` yazımıdır.

İstisnanın gerekçesi ikinci kuraldır: bu yazım tam olarak iki değeri kapsar, başka hiçbir
değeri yanlışlıkla içine almaz.

```js
const gelen = { istasyon: "A2", nem: undefined };
console.log("gevsek:", gelen.nem == null);
console.log("kati  :", gelen.nem === null);
```

```
gevsek: true
kati  : false
```

İki satır farklı soruları yanıtlıyor. Birincisi "bu alanın değeri yok mu"; ikincisi "bu
alan bilinçli olarak boş mu". İlkel Tipler dersindeki ayrım hangi soruyu sormak
gerektiğini belirler.

#### Değer Eşitliği

Üçüncü yol, katı eşitliğin iki istisnasını da ortadan kaldırır:

```js
console.log(Object.is(NaN, NaN));
console.log(Object.is(0, -0));
console.log(Object.is(21.5, 21.5));
```

```
true
false
true
```

`Object.is`, iki değerin **aynı değer** olup olmadığını sorar. `NaN` kendisiyle aynı
değerdir; `0` ile `-0` farklı değerlerdir. Bu, standardın birkaç yerinde iç karşılaştırma
olarak kullanılan bağıntıdır.

Günlük kullanımda `Object.is` gerekmez; `===` ile `Number.isNaN` birlikte aynı işi görür
ve daha okunaklıdır. Ayrımı bilmek, yerleşik yöntemlerin davranışını açıklamak için
gereklidir — örneğin dizi arama yöntemlerinin bir kısmı `NaN` bulabilirken bir kısmı
bulamaz, çünkü farklı bağıntılar kullanırlar.

#### Nesnelerde Karşılaştırma

Üç işlecin hiçbiri nesne içeriğini karşılaştırmaz. Nesneler için karşılaştırılan şey
**kimliktir**: iki başvurunun aynı nesneyi gösterip göstermediği.

```js
const a = { istasyon: "A1" };
const b = { istasyon: "A1" };
const c = a;
console.log(a === b);
console.log(a === c);
console.log(a == b);
```

```
false
true
false
```

`a` ile `b` aynı içeriğe sahiptir ama farklı nesnelerdir. `c`, `a` ile aynı nesneyi
gösterdiği için eşittir. Gevşek eşitlik de aynı sonucu verir: iki yan da nesne olduğu
için birinci kural devreye girer ve zorlama yapılmaz.

Programlama Temelleri kursundaki Değişkenler ve Bağlama dersinde kurulan kimlik–eşitlik
ayrımı burada dilin işleçlerine birebir yansımıştır. İçerik karşılaştırması gerekiyorsa
yazılması gerekir; dil bunu sağlamaz.

#### Ölçüm Betiğine Uygulama

Kayıtların geçerliliğini sınayan denetimler, doğru işleçle yazıldığında hem eksik hem
bozuk alanları ayırt eder:

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5, nem: null },
  { istasyon: "A2", sicaklik: 0, nem: 55 },
  { istasyon: "B1", sicaklik: NaN, nem: 41 },
];

for (const k of kayitlar) {
  const nemEksik = k.nem == null;
  const sicaklikGecersiz = Number.isNaN(k.sicaklik);
  console.log(k.istasyon, "nem eksik:", nemEksik, "sicaklik gecersiz:", sicaklikGecersiz);
}
```

```
A1 nem eksik: true sicaklik gecersiz: false
A2 nem eksik: false sicaklik gecersiz: false
B1 nem eksik: false sicaklik gecersiz: true
```

İkinci kayıttaki `0` değeri her iki denetimden de geçti — sıfır, ne eksik ne geçersizdir.
Bir önceki derste kısayol yazımının bozduğu tam olarak bu durumdu. Üçüncü kayıttaki `NaN`
ise yalnızca `Number.isNaN` ile yakalanabilir; `k.sicaklik === NaN` yazımı hiçbir zaman
doğru vermezdi.

#### Özet

- `===` önce tipe bakar; tipler farklıysa zorlama yapmadan yanlış verir.
- `NaN` hiçbir değere, kendisine bile eşit değildir; `0` ile `-0` katı eşitlikte eşittir.
- `==` beş kuralla ortak tipe iner; `null` ve `undefined` yalnızca birbirine eşittir;
  mantıksal değerler önce sayıya çevrilir; nesneler ilkel değere indirgenir.
- Gevşek eşitlik geçişli değildir; bu yüzden karşılaştırmalar `===` ile yazılır, tek
  istisna `deger == null` yazımıdır.
- `Object.is` değer eşitliğidir: `NaN` kendisine eşit, `0` ile `-0` farklıdır.
- Nesneler kimliğe göre karşılaştırılır; içerik karşılaştırması yazılmadıkça yapılmaz.

#### Sonraki Adım

Önceki derste bırakılan soru hâlâ açık: koşulun içine sayı, dizgi veya nesne konduğunda
ne olur? `if (deger)` yazımı bir eşitlik karşılaştırması değildir; değeri mantıksal tipe
çeviren üçüncü zorlama yordamını çağırır. Sonraki ders bu yordamın tam listesini verecek
ve `0` değerinin neden eksik veri sanıldığını gösterecek.

### Doğruluk Değerleri

Source: https://academia.sh/tr/kurslar/javascript-temelleri/dogruluk-degerleri

Mantıksal değere çevirme yordamı, yanlış sayılan sekiz değer, mantıksal işleçlerin değer döndürmesi, boşluk birleştirme ve isteğe bağlı zincirleme.

Önceki iki ders iki zorlama yordamını tanımladı: sayıya ve metne çevirme. Üçüncüsü,
mantıksal değere çevirme, koşullu yapıların temelidir ve iki derstir ertelenen bir soruyu
yanıtlar: `0` değeri neden eksik veri sanıldı?

Yordamın kendisi kısadır — sayfa dolusu kural yoktur; sekiz değerden oluşan bir liste
vardır. Asıl incelik listede değil, mantıksal işleçlerin bu listeyi nasıl kullandığındadır.

#### Yanlış Sayılan Sekiz Değer

Mantıksal değere çevirme yordamı bir liste ile tanımlıdır: aşağıdaki sekiz değer
**yanlış sayılır (falsy)**, diğer her değer doğru sayılır.

```js
const yanlisSayilanlar = [
  ["false", false],
  ["0", 0],
  ["-0", -0],
  ["0n", 0n],
  ['""', ""],
  ["null", null],
  ["undefined", undefined],
  ["NaN", NaN],
];

for (const [etiket, deger] of yanlisSayilanlar) {
  console.log(etiket.padEnd(10), Boolean(deger));
}
```

```
false      false
0          false
-0         false
0n         false
""         false
null       false
undefined  false
NaN        false
```

Liste kapalıdır: burada olmayan hiçbir değer yanlış sayılmaz. Bu, listeyi ezberlemekten
daha kullanışlı bir kuraldır, çünkü sezgiye aykırı örnekleri doğrudan çözer:

```js
console.log(Boolean("0"), Boolean("false"), Boolean(" "));
console.log(Boolean([]), Boolean({}));
console.log(Boolean(-1), Boolean(Infinity));
```

```
true true true
true true
true true
```

Boş olmayan her dizgi doğru sayılır — içeriği ne olursa olsun. `"0"` ve `"false"`
metinleri de bu kurala girer; sayıya veya mantıksal değere çevrilmezler. Boş dizi ve boş
nesne de doğru sayılır: nesneler listede yoktur, bu yüzden hepsi doğrudur. Sıfır dışındaki
her sayı, negatifler ve sonsuzluk dahil, doğru sayılır.

Bir noktaya dikkat: `Boolean([])` doğru verirken, önceki dersteki `[] == false`
karşılaştırması doğru vermişti. Çelişki yoktur — iki farklı yordam çalışıyor. Gevşek
eşitlik diziyi ilkel değere, sonra sayıya çevirir; mantıksal değere çevirme ise nesnenin
içine hiç bakmaz. Aynı değerin iki farklı bağlamda farklı sonuç vermesi, hangi yordamın
çağrıldığını bilmeyi zorunlu kılar.

#### Mantıksal İşleçler Mantıksal Değer Döndürmez

`&&` ve `||` işleçleri, adlarına rağmen `true` veya `false` üretmez. **İşlenenlerden
birini** döndürürler:

```js
console.log(0 || "yedek");
console.log("A1" || "yedek");
console.log(0 && "asla");
console.log("A1" && 21.5);
```

```
yedek
A1
0
21.5
```

Kural şöyledir. `||` işleci sol işleneni değerlendirir; doğru sayılıyorsa onu döndürür,
değilse sağ işleneni döndürür. `&&` işleci sol işleneni değerlendirir; yanlış sayılıyorsa
onu döndürür, değilse sağ işleneni döndürür.

Sağ işlenen yalnızca gerektiğinde değerlendirilir. Programlama Temelleri kursundaki
İşleçler ve İfadeler dersinde tanımlanan kısa devre değerlendirme budur. Üçüncü satırda
`"asla"` hiç değerlendirilmedi.

Sonucu mantıksal tipe zorlamak gerekiyorsa dönüşüm açıkça yazılır. Koşullu yapılar zaten
mantıksal değere çevirdiği için bu genellikle gereksizdir.

#### Varsayılan Değer Tuzağı

`||` işlecinin en yaygın kullanımı eksik değere yedek koymaktır. Bu kullanım, yanlış
sayılan değerler geçerli veri olduğunda bozulur:

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5 },
  { istasyon: "A2", sicaklik: 0 },
  { istasyon: "B1" },
];

for (const k of kayitlar) {
  const veyaIle = k.sicaklik || -273.15;
  const bosIle = k.sicaklik ?? -273.15;
  console.log(k.istasyon, "|| ->", veyaIle, " ?? ->", bosIle);
}
```

```
A1 || -> 21.5  ?? -> 21.5
A2 || -> -273.15  ?? -> 0
B1 || -> -273.15  ?? -> -273.15
```

Orta satır, iki derstir açık bırakılan sorunun yanıtıdır. `A2` istasyonunun sıcaklığı
`0`'dır; bu geçerli bir ölçümdür ve kayıtta vardır. Yine de `||` işleci onu yanlış saydı
ve yedek değere geçti. Ölçüm, mutlak sıfır sıcaklığına dönüştü.

Alt satırdaki `B1` kaydında ise sıcaklık alanı gerçekten yoktur; `k.sicaklik` değeri
`undefined`'dır ve yedek değere geçmesi doğrudur. İki satır aynı sonucu üretti, ama
yalnızca biri istenen davranıştı — `||` işleci "değer yok" ile "değer sıfır" durumlarını
ayırt edemez.

`??` işleci — **boşluk birleştirme (nullish coalescing)** — yalnızca `null` ve
`undefined` değerlerinde sağ işlenene geçer:

```js
console.log(0 || "yedek");
console.log(0 ?? "yedek");
console.log("" || "yedek");
console.log("" ?? "yedek");
console.log(null ?? "yedek");
```

```
yedek
0
yedek

yedek
```

Dördüncü satırın boş görünmesi, `??` işlecinin boş dizgiyi geçerli bir değer sayıp
döndürmesindendir. İkinci satır ise `0` değerini korudu.

Seçim kuralı nettir: **"değer verilmemişse" anlamı isteniyorsa `??`, "değer yanlış
sayılıyorsa" anlamı isteniyorsa `||` kullanılır.** Ölçüm verisinde sıfır, boş dizgi ve
`false` çoğu zaman geçerli değerlerdir; bu yüzden varsayılan atamaları `??` ile yazılır.

#### İsteğe Bağlı Zincirleme

Aynı ailenin üçüncü işleci, iç içe alanlara erişirken yokluk denetimini kısaltır:

```js
const kayit = { istasyon: "A1", konum: { yukseklik: 120 } };
const eksik = { istasyon: "B1" };
console.log(kayit.konum?.yukseklik);
console.log(eksik.konum?.yukseklik);
console.log(eksik.konum?.yukseklik ?? 0);
```

```
120
undefined
0
```

`?.` işleci, solundaki değer `null` veya `undefined` ise zinciri keser ve `undefined`
döndürür; değilse erişimi sürdürür. Denetimin ölçütü `??` ile aynıdır — yanlış sayılan
değerler değil, yalnızca iki yokluk değeri.

İşleç olmadan aynı erişim hata verir:

```js
const eksik = { istasyon: "B1" };
try {
  console.log(eksik.konum.yukseklik);
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError: Cannot read properties of undefined (reading 'yukseklik')
```

Kod Çalıştırma Yolları dersinde bu iletinin nasıl okunacağı gösterilmişti: sorun
`yukseklik` alanında değil, `eksik.konum` değerinin `undefined` olmasındadır.

İsteğe bağlı zincirleme dikkatli kullanılmalıdır. Bir alanın eksik olması beklenen bir
durumsa doğru araçtır; beklenmeyen bir durumsa, hatayı bastırıp sorunu ileriye taşır.
Aradaki fark verinin sözleşmesindedir, sözdiziminde değil.

#### Ölçüm Betiğine Uygulama

Doğruluk değerlerinin en kısa kullanımı süzme işlemidir:

```js
const alanlar = ["21.5", "", "0", "abc", null];
const dolular = alanlar.filter(Boolean);
console.log(dolular);
```

```
[ '21.5', '0', 'abc' ]
```

Boş dizgi ve `null` elendi; `"0"` metni korundu, çünkü boş olmayan bir dizgidir. Bu tam
olarak istenen davranıştır: burada süzülen şey ham metindir, sayısal değer değil.

Sayısal değerlere aynı süzgeci uygulamak ise veri kaybettirir:

```js
const olcumler = [21.5, 0, NaN, 19.75];
console.log(olcumler.filter(Boolean));
console.log(olcumler.filter((d) => !Number.isNaN(d)));
```

```
[ 21.5, 19.75 ]
[ 21.5, 0, 19.75 ]
```

İlk süzgeç geçerli bir sıfır ölçümünü attı. İkincisi yalnızca gerçekten geçersiz olanı
attı. Kural, doğruluk değerlerinin genel bir geçerlilik ölçütü olmamasıdır: neyin geçerli
sayıldığı verinin anlamına bağlıdır ve açıkça yazılmalıdır.

#### Özet

- Mantıksal değere çevirme sekiz değeri yanlış sayar: `false`, `0`, `-0`, `0n`, boş
  dizgi, `null`, `undefined`, `NaN`. Diğer her değer doğru sayılır.
- Boş dizi, boş nesne ve `"0"` gibi metinler doğru sayılır; nesneler listede yoktur.
- `&&` ve `||` mantıksal değer değil, işlenenlerden birini döndürür ve sağ işleneni
  yalnızca gerektiğinde değerlendirir.
- `||` sekiz değerin tamamında yedeğe geçer; `??` yalnızca `null` ve `undefined`
  durumunda geçer. Ölçüm verisinde varsayılan atamaları `??` ile yazılır.
- `?.` zinciri yalnızca `null` ve `undefined` değerlerinde keser; beklenmeyen eksikliği
  gizlemek için kullanılmamalıdır.

#### Sonraki Adım

Değer modeli tamamlandı: hangi tipler var, nasıl dönüşüyorlar, nasıl karşılaştırılıyorlar
ve koşulda nasıl değerlendiriliyorlar. Sıra bu değerlerle program akışı kurmakta. Sonraki
konu koşullu seçimle başlayacak: `if` zincirleri, üçlü işleç ve `switch` deyiminin
karşılaştırma için hangi eşitliği kullandığı — önceki dersteki üç işleçten biri.

## Denetim Akışı ve Fonksiyonlar

Koşullu seçim, döngü biçimleri, fonksiyon tanım biçimlerinin davranış farkı, parametre kuralları, ok fonksiyonları ve hata yönetimi.

### Koşullar ve Seçim

Source: https://academia.sh/tr/kurslar/javascript-temelleri/kosullar-ve-secim

`if` zincirleri ve koruma cümleleri, üçlü işleç, `switch` deyiminin katı eşitliği, düşme davranışı ve durum bloklarında kapsam.

Değer modeli tamamlandı. Bu konu, o değerlerle program akışı kurmayı ele alıyor: hangi
kodun hangi koşulda çalışacağı, tekrarın nasıl yazılacağı ve işin nasıl fonksiyonlara
bölüneceği. Programlama Temelleri kursundaki Koşullu Dallanma dersinde bu yapıların
amacı kurulmuştu; burada JavaScript'e özgü kurallar ele alınacak.

Koşullu seçimde dile özgü olan üç şey vardır: koşulun mantıksal değere nasıl çevrildiği
(önceki ders), üçlü işlecin bir ifade olması ve `switch` deyiminin hangi eşitliği
kullandığı.

#### Koşul Zinciri ve Koruma Cümleleri

`if` deyimi, parantez içindeki ifadeyi mantıksal değere çevirir ve doğru sayılıyorsa
gövdeyi çalıştırır. Zincir `else if` ile sürdürülür.

Zincirin başına konan **koruma cümleleri (guard clause)** — geçersiz durumu erken
döndüren koşullar — asıl mantığı iç içe bloklardan kurtarır:

```js
function siniflandir(sicaklik) {
  if (sicaklik == null || Number.isNaN(sicaklik)) {
    return "gecersiz";
  }
  if (sicaklik < 18) {
    return "dusuk";
  } else if (sicaklik < 22) {
    return "normal";
  } else {
    return "yuksek";
  }
}

for (const d of [17.9, 18, 21.5, 22, 23, null, NaN]) {
  console.log(String(d).padEnd(5), siniflandir(d));
}
```

```
17.9  dusuk
18    normal
21.5  normal
22    yuksek
23    yuksek
null  gecersiz
NaN   gecersiz
```

Koruma cümlesi iki denetimi birleştiriyor. `== null` yazımı Eşitlik Karşılaştırmaları
dersinde tanımlanan istisnadır: hem `null` hem `undefined` durumunu kapsar.
`Number.isNaN` ise geçersiz sayıyı yakalar; `NaN` hiçbir karşılaştırmayı geçemediği için
korunmasa `siniflandir(NaN)` sessizce `"yuksek"` verirdi — her üç karşılaştırma da yanlış
çıkar ve akış son `else` dalına düşerdi.

Sınır değerlerinin hangi tarafa düştüğü çıktıdan okunuyor: `18` normale, `22` yükseğe
gitti. Karşılaştırma işleçlerinin seçimi (`<` mi `<=` mi) bu sınırları belirler ve
tanımın parçasıdır.

Süslü ayraçlar tek deyimlik gövdelerde zorunlu değildir, ancak kursta her zaman
yazılacaktır. Gerekçe, sonradan eklenen ikinci deyimin sessizce koşulun dışında
kalmasıdır.

#### Üçlü İşleç

`if` bir deyimdir: değer üretmez, akışı yönlendirir. Üçlü işleç ise bir **ifadedir**:
değer üretir ve değer beklenen her yere yazılabilir.

```js
const sicaklik = 21.5;
const durum = sicaklik >= 22 ? "yuksek" : sicaklik >= 18 ? "normal" : "dusuk";
console.log(durum);
console.log(`${sicaklik} derece: ${sicaklik > 20 ? "esik ustu" : "esik alti"}`);
```

```
normal
21.5 derece: esik ustu
```

İkinci satır, ifade olmanın somut faydasını gösteriyor: metin şablonunun içine bir koşul
yerleştirildi. `if` deyimi oraya yazılamazdı.

Zincirlenmiş üçlü işleç sağa doğru birleşir; ikinci soru işareti, birinci ifadenin yanlış
dalının içindedir. Bu yazım ikiden fazla dalda okunaklılığını hızla kaybeder. Kursun
kuralı: **üçlü işleç en fazla bir kez zincirlenir**, daha fazlası `if` zinciriyle ya da
bir eşleme nesnesiyle yazılır.

#### `switch` Deyimi

`switch`, bir değeri birden çok sabitle karşılaştırır. Eşleştirme **katı eşitlikle**
yapılır — Eşitlik Karşılaştırmaları dersindeki üç işleçten biri, `===`.

```js
function birim(alan) {
  switch (alan) {
    case "sicaklik":
      return "C";
    case "nem":
      return "%";
    case "basinc":
      return "hPa";
    default:
      return "bilinmiyor";
  }
}
console.log(birim("sicaklik"), birim("nem"), birim("ruzgar"));
```

```
C % bilinmiyor
```

Katı eşitlik kullanılması, zorlama beklentisini ortadan kaldırır:

```js
function sinama(deger) {
  switch (deger) {
    case 1:
      return "sayi bir";
    case "1":
      return "metin bir";
    default:
      return "eslesmedi";
  }
}
console.log(sinama(1), "|", sinama("1"), "|", sinama(true));
```

```
sayi bir | metin bir | eslesmedi
```

Sayı `1` ile metin `"1"` ayrı dallara gitti. `true` değeri hiçbirine eşleşmedi — gevşek
eşitlikte `true == 1` doğru olurdu, ama `switch` gevşek eşitlik kullanmaz.

#### Düşme

`switch` deyimi eşleşen `case` etiketinden başlar ve **bir `break` ya da `return` ile
karşılaşana kadar aşağı doğru akar**. Bu davranışa düşme (fall-through) denir ve
kasıtsız yapıldığında sessiz hata üretir:

```js
function etiket(alan) {
  let sonuc = "";
  switch (alan) {
    case "sicaklik":
      sonuc = "sicaklik (C)";
    case "nem":
      sonuc += " nem (%)";
      break;
    default:
      sonuc = "bilinmiyor";
  }
  return sonuc;
}
console.log(JSON.stringify(etiket("sicaklik")));
console.log(JSON.stringify(etiket("nem")));
console.log(JSON.stringify(etiket("basinc")));
```

```
"sicaklik (C) nem (%)"
" nem (%)"
"bilinmiyor"
```

İlk satırda akış birinci daldan ikinciye düştü ve iki metin birleşti. Yazarın niyeti bu
değildi. Çıktılar `JSON.stringify` ile yazıldı; aksi halde ikinci satırdaki baştaki
boşluk görünmezdi.

Düşme bilinçli kullanıldığında birden çok değeri aynı dala bağlar:

```js
function grup(istasyon) {
  switch (istasyon) {
    case "A1":
    case "A2":
      return "A grubu";
    case "B1":
      return "B grubu";
    default:
      return "bilinmiyor";
  }
}
console.log(grup("A1"), "|", grup("A2"), "|", grup("B1"), "|", grup("C9"));
```

```
A grubu | A grubu | B grubu | bilinmiyor
```

Boş `case` etiketleri gövdesizdir; akış doğrudan bir sonrakine düşer. Bu, dilin kabul
edilen kullanımıdır ve okunması güç değildir — gövdeli bir daldan diğerine düşmek ise
kursta yazılmayacaktır.

#### Durum Bloklarında Kapsam

`switch` gövdesinin tamamı **tek bir bloktur**. Ayrı `case` etiketleri ayrı kapsam
açmaz; bu yüzden iki dalda aynı adı bildirmek ayrıştırma hatası verir:

```js
switch (1) {
  case 1:
    const x = 1;
    break;
  case 2:
    const x = 2;
    break;
}
```

```
SyntaxError: Identifier 'x' has already been declared
```

Değişken Bildirimleri dersindeki yeniden bildirim kuralı burada beklenmedik bir yerde
karşımıza çıkıyor. Çözüm, her dalı süslü ayraçlarla kendi bloğuna almaktır:

```js
switch (1) {
  case 1: {
    const x = "birinci";
    console.log(x);
    break;
  }
  case 2: {
    const x = "ikinci";
    console.log(x);
    break;
  }
}
```

```
birinci
```

#### Seçim Yapısının Seçimi

Üç yapı arasındaki seçim, karşılaştırmanın biçimine göre yapılır.

`switch`, tek bir değeri sabitlerle karşılaştırdığında uygundur. Aralık denetimi,
birleşik koşul veya sıfırdan farklı bir karşılaştırma gerekiyorsa `if` zinciri gerekir.

Sabit eşlemeler, üçüncü bir seçenek sunar: bir nesneye anahtar–değer olarak yazılıp
doğrudan okunabilirler.

```js
const BIRIMLER = { sicaklik: "C", nem: "%", basinc: "hPa" };

function birim(alan) {
  return BIRIMLER[alan] ?? "bilinmiyor";
}
console.log(birim("sicaklik"), birim("ruzgar"));
```

```
C bilinmiyor
```

Bu yazım, eşleme büyüdükçe okunaklılığını korur ve veri ile kodu ayırır. `??` işlecinin
kullanılması bilinçlidir: eşlemede karşılığı `0` veya boş dizgi olan bir anahtar
bulunsaydı, `||` yazımı onu da varsayılana düşürürdü.

#### Özet

- Koruma cümleleri geçersiz durumu erken döndürür ve asıl mantığı iç içe bloklardan
  kurtarır; `NaN` denetimi korunmazsa akış sessizce son dala düşer.
- Üçlü işleç bir ifadedir ve değer beklenen her yere yazılabilir; en fazla bir kez
  zincirlenir.
- `switch` katı eşitlikle eşleştirir; zorlama yapmaz.
- Akış eşleşen etiketten başlar ve `break` ya da `return` görene kadar düşer; gövdesiz
  etiketler birden çok değeri aynı dala bağlar.
- `switch` gövdesi tek bir bloktur; dallarda ad bildirmek için her dal kendi süslü
  ayraçlarına alınır.
- Sabit eşlemeler nesne olarak yazıldığında `switch` yerine geçer ve `??` ile varsayılan
  alır.

#### Sonraki Adım

Koşullar tek bir kaydı sınıflandırdı. Ölçüm listesinin tamamını işlemek için tekrar
gerekiyor. Sonraki ders döngü biçimlerini ele alacak: sayaçlı döngü, koşullu döngü ve iki
farklı koleksiyon döngüsü. İki koleksiyon döngüsü arasındaki fark — birinin anahtarları,
diğerinin değerleri vermesi — dizilerde beklenmedik sonuçlar üretir.

### Döngüler

Source: https://academia.sh/tr/kurslar/javascript-temelleri/donguler

Sayaçlı, koşullu ve koleksiyon döngüleri; `for...in` ile `for...of` ayrımı, döngü denetimi, etiketli çıkış ve nesneler üzerinde gezinme.

Önceki ders tek bir ölçümü sınıflandırdı. Listenin tamamını işlemek için tekrar gerekir.
Programlama Temelleri kursundaki Döngüler ve Döngü Denetimi derslerinde bu yapıların
amacı ve döngü değişmezi kavramı kurulmuştu; burada JavaScript'in sunduğu biçimler ve
aralarındaki — özellikle iki koleksiyon döngüsü arasındaki — fark ele alınıyor.

Beş döngü biçimi vardır ve seçim keyfi değildir: sayaçlı döngü dizin gerektiğinde,
koşullu döngüler tekrar sayısı önceden bilinmediğinde (gövdenin en az bir kez çalışması
gerekiyorsa koşulu sona alan biçimiyle), koleksiyon döngüleri ise doğrudan elemanlar
üzerinde çalışıldığında kullanılır.

#### Sayaçlı ve Koşullu Döngüler

Klasik `for` döngüsü üç bölümden oluşur: başlatma, koşul ve tur sonu işlemi. Değişken
Bildirimleri dersinde gösterildiği gibi, sayaç `let` ile bildirilir ve her turda yeniden
bağlanır.

```js
const sicakliklar = [21.5, 19.75, 23, 18.25];

for (let i = 0; i < sicakliklar.length; i++) {
  console.log(i, sicakliklar[i]);
}
```

```
0 21.5
1 19.75
2 23
3 18.25
```

`while` döngüsü yalnızca koşul taşır; tekrar sayısı önceden bilinmediğinde kullanılır:

```js
const sicakliklar = [21.5, 19.75, 23, 18.25];
let i = 0;
while (i < sicakliklar.length && sicakliklar[i] < 23) {
  i += 1;
}
console.log("ilk 23 ve ustu dizin:", i);
```

```
ilk 23 ve ustu dizin: 2
```

Koşuldaki sıra önemlidir. Sınır denetimi (`i < sicakliklar.length`) önce yazılmıştır;
kısa devre değerlendirme sayesinde dizin aşıldığında ikinci karşılaştırma hiç
yapılmaz. Ters sırada yazılsaydı, dizinin sonunda `undefined < 23` karşılaştırması
yapılırdı — bu ifade `NaN` üreten bir zorlamadan geçtiği için yanlış verir ve döngü yine
biterdi, ama gerekçesi denetim değil rastlantı olurdu.

`do...while` biçimi gövdeyi en az bir kez çalıştırır; koşul tur sonunda sınanır:

```js
let tur = 0;
do {
  tur += 1;
} while (tur < 3);
console.log("tur:", tur);
```

```
tur: 3
```

#### İki Koleksiyon Döngüsü

JavaScript'te birbirine benzeyen iki koleksiyon döngüsü vardır ve **farklı şeyler
verirler**.

`for...in`, nesnenin **sayılabilir özellik anahtarlarını** verir. `for...of`, yinelenebilir
bir değerin **elemanlarını** verir.

```js
const sicakliklar = [21.5, 19.75, 23];

for (const dizin in sicakliklar) {
  console.log(typeof dizin, dizin);
}
for (const deger of sicakliklar) {
  console.log(typeof deger, deger);
}
```

```
string 0
string 1
string 2
number 21.5
number 19.75
number 23
```

İlk döngü dizinleri verdi ve dizinler **dizgi tipindedir**. Bunun nedeni nesne
anahtarlarının dizgi olmasıdır; dizi de bir nesnedir ve dizinleri anahtar olarak taşır.
Bu, `for...in` ile toplama yapan kodun neden metin birleştirdiğini açıklar — Tip Dönüşümü
ve Zorlama dersindeki toplama kuralı devreye girer.

Fark, diziye bir özellik eklendiğinde daha da belirginleşir:

```js
const sicakliklar = [21.5, 19.75, 23];
sicakliklar.kaynak = "A1";

for (const dizin in sicakliklar) {
  console.log("for...in:", dizin);
}
for (const deger of sicakliklar) {
  console.log("for...of:", deger);
}
console.log("length:", sicakliklar.length);
```

```
for...in: 0
for...in: 1
for...in: 2
for...in: kaynak
for...of: 21.5
for...of: 19.75
for...of: 23
length: 3
```

`for...in` dizi elemanı olmayan bir anahtarı da dolaştı; `for...of` yalnızca elemanları
verdi. `length` değeri de değişmedi — eklenen özellik dizinin uzunluğuna sayılmaz.

Kursun kuralı buradan çıkar: **diziler üzerinde `for...of` kullanılır.** `for...in`
yalnızca sıradan nesnelerin anahtarlarını dolaşmak için yazılır.

Dizin ile değeri birlikte isteyen durumda ayrı bir yöntem vardır:

```js
const sicakliklar = [21.5, 19.75, 23];
for (const [dizin, deger] of sicakliklar.entries()) {
  console.log(dizin, deger);
}
```

```
0 21.5
1 19.75
2 23
```

Burada dizin sayı tipindedir. Köşeli ayraçlı yazım, çiftin iki bileşenini iki ada
ayırır; bu **yapıbozum (destructuring)** biçimi Diziler ve Nesneler derslerinde ayrıntılı
ele alınacak.

#### Nesneler Üzerinde Gezinme

Sıradan nesneler `for...of` ile dolaşılamaz:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5, nem: 48 };
try {
  for (const d of kayit) {
    console.log(d);
  }
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError: kayit is not iterable
```

İleti kesindir: nesne **yinelenebilir (iterable)** değildir. Diziler, dizgiler ve bazı
yerleşik koleksiyonlar yinelenebilirdir; sıradan nesneler değildir.

İki yol vardır:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5, nem: 48 };

for (const anahtar in kayit) {
  console.log(anahtar, kayit[anahtar]);
}
for (const [anahtar, deger] of Object.entries(kayit)) {
  console.log(anahtar, "=", deger);
}
```

```
istasyon A1
sicaklik 21.5
nem 48
istasyon = A1
sicaklik = 21.5
nem = 48
```

İkisi aynı sonucu verdi, ama kapsamları aynı değil. `for...in` kalıtılan özellikleri de
dolaşır; `Object.entries` yalnızca nesnenin kendi özelliklerini verir. Kalıtım zinciri
bir sonraki kursun konusudur; şimdilik ikinci biçimin daha dar ve daha öngörülebilir
olduğunu bilmek yeterlidir. Kursta nesne gezinmesi `Object.entries` ile yazılacaktır.

#### Döngü Denetimi

`break` döngüyü bitirir, `continue` o turu atlar. İkisi de en içteki döngüye etki eder.

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5 },
  { istasyon: "A2", sicaklik: NaN },
  { istasyon: "B1", sicaklik: 23 },
  { istasyon: "B2", sicaklik: 18.25 },
];

let toplam = 0;
let sayac = 0;
for (const kayit of kayitlar) {
  if (Number.isNaN(kayit.sicaklik)) {
    continue;
  }
  toplam += kayit.sicaklik;
  sayac += 1;
}
console.log("gecerli okuma:", sayac, "ortalama:", toplam / sayac);

let ilkYuksek = null;
for (const kayit of kayitlar) {
  if (kayit.sicaklik > 22) {
    ilkYuksek = kayit.istasyon;
    break;
  }
}
console.log("ilk yuksek:", ilkYuksek);
```

```
gecerli okuma: 3 ortalama: 20.916666666666668
ilk yuksek: B1
```

Birinci döngü geçersiz kaydı atladı ve ortalamayı yalnızca geçerli okumalar üzerinden
hesapladı — bölen olarak `kayitlar.length` yerine `sayac` kullanılması bu yüzden
zorunludur. İkinci döngü aradığını bulunca durdu; kalan kayıtlara bakılmadı.

Ortalamanın basamakları, İlkel Tipler dersindeki kayan noktalı gösterimin sonucudur:
`62.75 / 3` işleminin sonucu ikilik kesir olarak tam yazılamaz ve en yakın temsil
edilebilir değere yuvarlanır.

İç içe döngüde dıştakini kesmek için **etiket** kullanılır:

```js
const izgara = [[1, 2], [3, 4], [5, 6]];
disDongu: for (const satir of izgara) {
  for (const hucre of satir) {
    if (hucre === 4) {
      console.log("bulundu, iki dongu de kesiliyor");
      break disDongu;
    }
    console.log("bakilan:", hucre);
  }
}
```

```
bakilan: 1
bakilan: 2
bakilan: 3
bulundu, iki dongu de kesiliyor
```

Etiket, döngünün önüne konan bir addır; `break` ve `continue` bu adı alarak hangi döngüye
etki edeceğini belirtir. Etiketsiz `break` yalnızca iç döngüyü keser ve dış döngü bir
sonraki satıra geçerdi.

Etiketli çıkış seyrek gerekir. Aynı işi bir fonksiyon içine alıp `return` ile çıkmak
çoğu zaman daha okunaklıdır; fonksiyon tanımı bir sonraki dersin konusudur.

#### Özet

- Sayaçlı `for` dizin gerektiğinde, `while` tekrar sayısı bilinmediğinde, `do...while`
  gövdenin en az bir kez çalışması gerektiğinde kullanılır.
- `for...in` özellik anahtarlarını verir ve anahtarlar dizgi tipindedir; `for...of`
  elemanları verir.
- Dizilerde `for...of` kullanılır; `for...in` dizi olmayan özellikleri de dolaşır.
- Sıradan nesneler yinelenebilir değildir; anahtar–değer gezinmesi `Object.entries` ile
  yazılır.
- `break` ve `continue` en içteki döngüye etki eder; dıştakine ulaşmak için etiket
  kullanılır.

#### Sonraki Adım

Bu derste denetimler ve birikeçler döngü gövdesine gömüldü; aynı işi ikinci kez yapmak
gerektiğinde kod kopyalanacaktı. Sonraki ders fonksiyonlara geçiyor ve JavaScript'e özgü
bir ayrımla başlıyor: bir fonksiyonu bildirim olarak yazmakla bir ifade olarak yazmak
arasındaki fark yalnızca sözdizimi değildir — adın ne zaman erişilebilir olduğunu da
belirler.

### Fonksiyon Bildirimi ve İfadesi

Source: https://academia.sh/tr/kurslar/javascript-temelleri/fonksiyon-bildirimi-ve-ifadesi

İki tanım biçiminin yukarı taşıma farkı, adlı fonksiyon ifadeleri, fonksiyonların değer olarak kullanımı, dönüş kuralları ve otomatik noktalı virgül tuzağı.

Önceki derste denetimler ve birikeçler döngü gövdesine gömülüydü. Aynı işi ikinci kez
yapmak gerektiğinde kod kopyalanacaktı. Programlama Temelleri kursundaki Fonksiyon Tanımı
ve Çağrı dersinde bu bölmenin gerekçesi kurulmuştu.

JavaScript'te fonksiyon tanımlamanın birden çok biçimi vardır ve aralarındaki fark
sözdiziminden ibaret değildir. Bu ders iki temel biçimi karşılaştırır; üçüncü biçim olan
ok fonksiyonları bu konunun beşinci dersinde ele alınacak.

#### İki Tanım Biçimi

**Fonksiyon bildirimi**, `function` anahtar sözcüğüyle başlayan ve bir ad taşıyan
deyimdir. **Fonksiyon ifadesi**, bir değer üreten ve genellikle bir değişkene atanan
biçimdir.

Adlandırma ve Kapsam Kuralları dersinde gösterilen fark burada tekrar edilir: bildirim
gövdesiyle birlikte yukarı taşınır, ifade taşınmaz.

```js
console.log(ortalamaBildirim([21.5, 19.75]));

function ortalamaBildirim(dizi) {
  let toplam = 0;
  for (const d of dizi) toplam += d;
  return toplam / dizi.length;
}

const ortalamaIfade = function (dizi) {
  let toplam = 0;
  for (const d of dizi) toplam += d;
  return toplam / dizi.length;
};
console.log(ortalamaIfade([21.5, 19.75]));
```

```
20.625
20.625
```

Bildirim, tanımından önceki satırdan çağrılabildi. İfade, atama satırından önce
çağrılsaydı zamansal ölü bölge hatası verirdi.

İki biçimin ikinci farkı, ifadenin bir **değer** olmasıdır: doğrudan bir argüman olarak
geçilebilir, bir nesnenin özelliği yapılabilir ya da bir fonksiyondan döndürülebilir.
Bildirim bunu yapamaz; önce bir ada bağlanması gerekir.

Kursun kuralı: **en dış kapsamdaki yardımcı fonksiyonlar bildirim biçiminde yazılır**;
başka bir fonksiyona geçirilen veya bir değişkende taşınan fonksiyonlar ifade biçiminde
yazılır. Gerekçe, bildirimin çağrı sırasından bağımsız olması ve dosyanın okunma sırasını
serbest bırakmasıdır.

#### Adlı Fonksiyon İfadeleri

Bir fonksiyon ifadesi ad taşıyabilir. Bu ad, yalnızca fonksiyonun **kendi gövdesinde**
görülür:

```js
const topla = function toplayici(dizi, dizin = 0) {
  if (dizin >= dizi.length) return 0;
  return dizi[dizin] + toplayici(dizi, dizin + 1);
};
console.log(topla([21.5, 19.75, 23]));
console.log(topla.name);
try {
  console.log(toplayici([1]));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
64.25
toplayici
ReferenceError: toplayici is not defined
```

İç ad özyinelemeli çağrıyı mümkün kıldı ve `name` özelliğinde göründü, ama dışarıdan
erişilemedi. Bu, özyinelemeli bir fonksiyon ifadesinin kendisini dış değişkene bağımlı
olmadan çağırmasını sağlar; dış değişken sonradan başka bir değere bağlansa bile
özyineleme çalışmayı sürdürür.

Fonksiyonlar iki bilgilendirici özellik taşır:

```js
function esikUstu(dizi, esik) { return dizi.filter((d) => d > esik); }
const anonim = function (a, b) { return a + b; };
console.log(esikUstu.name, esikUstu.length);
console.log(anonim.name, anonim.length);
console.log(typeof esikUstu, esikUstu instanceof Object);
```

```
esikUstu 2
anonim 2
function true
```

`name`, hata iletilerinde ve yığıt izlerinde görünen addır; atandığı değişkenin adından
çıkarılabilir. `length`, beklenen parametre sayısıdır — varsayılan değerli ve kalan
parametreler sayılmaz, bu bir sonraki dersin konusudur.

Son satır bir tip gözlemidir: fonksiyonlar nesnedir. `typeof` ayrı bir sonuç verse de,
özellik taşırlar ve nesne olarak davranırlar.

#### Fonksiyonlar Değerdir

Fonksiyonun değer olması, onu argüman olarak geçirmeyi ve bir fonksiyondan
döndürmeyi mümkün kılar. Programlama Temelleri kursundaki Fonksiyonel Programlamaya
Giriş dersinde tanımlanan yüksek mertebeden fonksiyon kavramı budur.

```js
const sicakliklar = [21.5, 19.75, 23, 18.25];

function esikSuzgeci(esik) {
  return function (deger) {
    return deger > esik;
  };
}

const yirmiUstu = esikSuzgeci(20);
console.log(sicakliklar.filter(yirmiUstu));
console.log(sicakliklar.filter(esikSuzgeci(22)));
```

```
[ 21.5, 23 ]
[ 23 ]
```

Döndürülen fonksiyon, yaratıldığı çağrıdaki `esik` bağlamasını taşımayı sürdürüyor. İki
ayrı çağrı iki ayrı bağlama üretti; ilki `20`, ikincisi `22` gördü. Bu davranışın adı
**kapanıştır** ve bir sonraki kursun temel konularından biridir. Burada yalnızca
kullanılıyor.

Bir fonksiyon ifadesi tanımlandığı yerde hemen çağrılabilir. Bu kalıp, bir adı dış
kapsama sızdırmadan iş yapmak için kullanılır:

```js
const rapor = (function () {
  const gizli = [21.5, 19.75];
  return { adet: gizli.length };
})();
console.log(rapor);
console.log(typeof gizli);
```

```
{ adet: 2 }
undefined
```

`gizli` adı dışarıda görünmüyor. Modül kipi bu ihtiyacın çoğunu ortadan kaldırır — Kod
Çalıştırma Yolları dersinde görüldüğü gibi modülün en dış kapsamı zaten özeldir — ama
kalıp, dar bir kapsam açmak için hâlâ kullanılır.

#### Blok İçinde Bildirim

Katı kipte fonksiyon bildirimleri **blok kapsamlıdır**:

```js
if (true) {
  function blokIcinde() {
    return "blok";
  }
  console.log(blokIcinde());
}
try {
  console.log(blokIcinde());
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
blok
ReferenceError: blokIcinde is not defined
```

Katı kipte olmayan betiklerde bu davranış farklıdır ve uyumluluk kuralları devreye
girer. Belirsizlikten kaçınmanın yolu, koşula bağlı tanımları bildirim yerine ifadeyle
yazmaktır: değişken bildiriminin kapsam kuralı her iki kipte de aynıdır.

#### Dönüş Kuralları

Dönüş değeri yazılmamış bir fonksiyon `undefined` döndürür. Bu, çağıranın gördüğü
değerdir ve fonksiyonun bir iş yapıp yapmadığından bağımsızdır.

```js
function yaz(kayit) {
  console.log(kayit.istasyon, kayit.sicaklik);
}
const sonuc = yaz({ istasyon: "A1", sicaklik: 21.5 });
console.log("donus:", sonuc);

function erkenCikis(deger) {
  if (deger == null) return;
  return deger * 2;
}
console.log(erkenCikis(null), erkenCikis(10));
```

```
A1 21.5
donus: undefined
undefined 20
```

Programlama Temelleri kursundaki komut–sorgu ayrımı burada da geçerlidir: bir fonksiyon
ya iş yapar ya değer döndürür. `yaz` iş yapar; dönüşünü kullanmak anlamsızdır.

#### Otomatik Noktalı Virgül Tuzağı

JavaScript, belirli durumlarda satır sonuna noktalı virgül ekler. Kural `return` deyiminde
sert bir sonuç doğurur: **`return` ile döndürülecek ifade arasına satır sonu
konamaz.**

```js
function hataliDonus() {
  return
    21.5;
}
function dogruDonus() {
  return 21.5;
}
console.log(hataliDonus());
console.log(dogruDonus());
```

```
undefined
21.5
```

İlk fonksiyonda `return` deyimi kendi satırında sonlandırıldı; `21.5;` satırı hiç
çalışmayan ayrı bir deyim oldu. Hata verilmedi, uyarı çıkmadı — yalnızca yanlış değer
döndü.

Aynı kural `throw`, `break`, `continue` ve artırma işleçleri için de geçerlidir.
Korunmanın yolu, döndürülecek ifadeyi `return` ile aynı satırda başlatmaktır. Uzun bir
ifade döndürülüyorsa açılış parantezi aynı satıra konur.

#### Ölçüm Betiğine Uygulama

Önceki derslerdeki denetimler ve hesaplar fonksiyonlara ayrıldığında, betiğin gövdesi
akışı gösteren birkaç satıra iner:

```js
function gecerliMi(kayit) {
  return kayit.sicaklik != null && !Number.isNaN(kayit.sicaklik);
}

function ortalama(kayitlar) {
  let toplam = 0;
  let sayac = 0;
  for (const kayit of kayitlar) {
    if (!gecerliMi(kayit)) continue;
    toplam += kayit.sicaklik;
    sayac += 1;
  }
  return sayac === 0 ? null : toplam / sayac;
}

const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5 },
  { istasyon: "A2", sicaklik: NaN },
  { istasyon: "B1", sicaklik: 23 },
  { istasyon: "B2", sicaklik: null },
];

console.log("gecerli:", kayitlar.filter(gecerliMi).length);
console.log("ortalama:", ortalama(kayitlar));
console.log("bos liste:", ortalama([]));
```

```
gecerli: 2
ortalama: 22.25
bos liste: null
```

`gecerliMi` hem `filter` çağrısına argüman olarak geçti hem de döngü içinde çağrıldı —
fonksiyonun değer olmasının doğrudan faydası. `ortalama` fonksiyonu boş listede `null`
döndürüyor; bölme yapılsaydı `NaN` üretirdi ve hata sessizce ilerlerdi.

#### Özet

- Fonksiyon bildirimi gövdesiyle birlikte yukarı taşınır; fonksiyon ifadesi atama
  satırında oluşur ve öncesinde ölü bölgededir.
- Adlı fonksiyon ifadesinin adı yalnızca kendi gövdesinde görülür ve özyinelemeyi dış
  değişkenden bağımsız kılar.
- Fonksiyonlar değerdir: argüman olarak geçirilir, döndürülür ve nesne olarak özellik
  taşır.
- Katı kipte blok içindeki fonksiyon bildirimleri blok kapsamlıdır; koşula bağlı tanımlar
  ifadeyle yazılır.
- Dönüş değeri yazılmamış fonksiyon `undefined` verir; `return` ile ifade arasına satır
  sonu konursa deyim orada biter ve sessizce `undefined` döner.

#### Sonraki Adım

Bu derste fonksiyonlar sabit sayıda parametreyle çağrıldı. Gerçek kullanımda argüman
eksik gelebilir, fazla gelebilir ya da sayısı önceden bilinmeyebilir. Sonraki ders
parametre kurallarını ele alacak: varsayılan değerlerin ne zaman devreye girdiği, kalan
parametrelerin toplanması ve eski argüman nesnesinin neden yeni yazımda kullanılmadığı.

### Parametreler

Source: https://academia.sh/tr/kurslar/javascript-temelleri/parametreler

Eksik ve fazla argümanlar, varsayılan değerlerin devreye girme koşulu, kalan parametreler ve yayma, argüman nesnesi ve yapıbozumlu parametreler.

Önceki derste fonksiyonlar sabit sayıda argümanla çağrıldı. JavaScript bu konuda
gevşektir: eksik argüman hata vermez, fazla argüman da vermez. Gevşekliğin bedeli,
eksikliğin sessiz kalmasıdır.

Bu ders parametre kurallarını ele alır. Programlama Temelleri kursundaki Fonksiyon
Tanımı ve Çağrı dersinde parametre–argüman ayrımı kurulmuştu; burada JavaScript'in bu
kavramları hangi sözdizimiyle karşıladığı ve hangi tuzakları taşıdığı gösterilecek.

#### Eksik ve Fazla Argüman

Çağrıda verilmeyen parametre `undefined` değerini alır. Fazla verilen argüman sessizce
yok sayılır:

```js
function rapor(istasyon, esik) {
  return `${istasyon} / ${esik}`;
}
console.log(rapor("A1"));
console.log(rapor("A1", 20, "fazla"));
```

```
A1 / undefined
A1 / 20
```

İki davranış da hata üretmedi. Bu, dilin bilinçli bir kararıdır ve fonksiyonların
argüman sayısına göre farklı davranmasını mümkün kılar. Bedeli, yazım hatasıyla eksik
kalan bir argümanın ancak `undefined` bir değer taşıyıp ilerideki bir işlemi bozduğunda
fark edilmesidir.

#### Varsayılan Değerler

Parametreye varsayılan değer verilebilir. **Varsayılan yalnızca argüman `undefined`
olduğunda devreye girer** — eksik olduğunda ya da açıkça `undefined` verildiğinde.

```js
function rapor(istasyon, esik = 20) {
  return `${istasyon} / ${esik}`;
}
console.log(rapor("A1"));
console.log(rapor("A1", undefined));
console.log(rapor("A1", null));
console.log(rapor("A1", 0));
console.log(rapor.length);
```

```
A1 / 20
A1 / 20
A1 / null
A1 / 0
1
```

Üçüncü ve dördüncü satırlar, Doğruluk Değerleri dersindeki `??` işlecinin ölçütünden de
dar bir ölçüt olduğunu gösteriyor: `null` bile varsayılanı tetiklemez. `0` değeri de
geçer — varsayılan değerler, `||` ile yazılmış yedek atamalarının bozduğu durumu doğal
olarak korur.

Son satır, `length` özelliğinin varsayılan değerli parametreden **önceki** parametre
sayısını verdiğini gösteriyor.

Varsayılan ifadeler **her çağrıda yeniden değerlendirilir** ve kendilerinden önceki
parametreleri kullanabilir:

```js
let sayac = 0;
function sonraki() {
  sayac += 1;
  return sayac;
}
function etiketle(ad, sira = sonraki()) {
  return `${ad}-${sira}`;
}
console.log(etiketle("A1"));
console.log(etiketle("A2"));
console.log(etiketle("B1", 99));
console.log(etiketle("B2"));
```

```
A1-1
A2-2
B1-99
B2-3
```

Üçüncü çağrıda argüman verildiği için `sonraki` hiç çağrılmadı; sayaç artmadı. Bu,
varsayılanın çağrı anında ve yalnızca gerektiğinde değerlendirildiğinin kanıtıdır.

Önceki parametreye dayanan varsayılan, aralık tanımlarında kullanışlıdır:

```js
function aralik(alt, ust = alt + 5) {
  return [alt, ust];
}
console.log(aralik(18));
console.log(aralik(18, 30));
```

```
[ 18, 23 ]
[ 18, 30 ]
```

Sıra tek yönlüdür: bir parametre yalnızca kendisinden **önce** yazılmış parametrelere
başvurabilir.

Varsayılan ifadenin çağrı anında değerlendirilmesi, zorunlu parametreleri bildirmenin de
yolunu açar:

```js
function zorunlu(ad) {
  throw new Error(`${ad} parametresi zorunlu`);
}
function kaydet(istasyon = zorunlu("istasyon"), sicaklik = zorunlu("sicaklik")) {
  return `${istasyon}=${sicaklik}`;
}
console.log(kaydet("A1", 21.5));
try {
  kaydet("A1");
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
A1=21.5
Error: sicaklik parametresi zorunlu
```

Argüman verildiğinde varsayılan ifade hiç çalışmaz; verilmediğinde hata fırlatılır.
Hata nesneleri ve `throw` deyimi bu konunun son dersinde ele alınacak.

#### Kalan Parametreler ve Yayma

Sayısı önceden bilinmeyen argümanlar **kalan parametre (rest parameter)** ile toplanır.
Üç noktalı yazım, kalan tüm argümanları gerçek bir diziye koyar:

```js
function enYuksek(etiket, ...okumalar) {
  return `${etiket}: ${Math.max(...okumalar)} (${okumalar.length} okuma)`;
}
console.log(enYuksek("A1", 21.5, 19.75, 23));
console.log(enYuksek("B1"));
console.log(enYuksek.length);
```

```
A1: 23 (3 okuma)
B1: -Infinity (0 okuma)
1
```

İkinci satır bir sınır durumunu gösteriyor: argümansız çağrıda `okumalar` boş dizidir ve
`Math.max()` çağrısı `-Infinity` verir. Bu, matematiksel olarak tutarlıdır — boş kümenin
en büyük elemanı için nötr eleman döner — ama ölçüm bağlamında anlamsızdır. Boş liste
denetimi yazılmalıdır.

Aynı üç nokta, çağrı tarafında ters yönde çalışır: bir diziyi ayrı argümanlara açar.

```js
function enYuksek(etiket, ...okumalar) {
  return `${etiket}: ${Math.max(...okumalar)}`;
}
const sicakliklar = [21.5, 19.75, 23, 18.25];
console.log(enYuksek("hepsi", ...sicakliklar));
```

```
hepsi: 23
```

İki kullanım karıştırılmamalıdır. Parametre listesindeki üç nokta **toplar**; çağrıdaki
üç nokta **dağıtır**. Aynı sözdiziminin iki yönü, bulunduğu yere göre ayrılır.

Kalan parametre yalnızca son sırada bulunabilir ve `length` özelliğine sayılmaz.

#### Argüman Nesnesi

Kalan parametrelerden önce, değişken sayıda argüman `arguments` adlı örtük bir nesneyle
okunurdu. Bu nesne hâlâ bulunur ve eski kodda karşılaşılır:

```js
function eskiBicim() {
  console.log(typeof arguments, arguments.length);
  console.log(Array.isArray(arguments));
  console.log(Array.from(arguments));
}
eskiBicim(21.5, 19.75);
```

```
object 2
false
[ 21.5, 19.75 ]
```

İkinci satır sorunun kendisidir: `arguments` bir dizi değildir. Uzunluğu ve dizinleri
vardır, ama dizi yöntemleri yoktur; kullanmak için önce diziye çevrilmesi gerekir.

İkinci sorun varsayılan değerlerle ortaya çıkar:

```js
function varsayilanli(a = 1) {
  console.log("arguments.length:", arguments.length, "a:", a);
}
varsayilanli();
varsayilanli(5);
```

```
arguments.length: 0 a: 1
arguments.length: 1 a: 5
```

`arguments` yalnızca **gerçekten verilen** argümanları sayar; varsayılanla dolan
parametreyi görmez. İki bilgi kaynağı ayrışır ve hangisinin okunacağı belirsizleşir.

Üçüncü olarak, `arguments` ok fonksiyonlarında hiç bulunmaz — bir sonraki dersin konusu.
Kursta değişken sayıda argüman her zaman kalan parametreyle yazılacaktır.

#### Yapıbozumlu Parametreler

Üç dörtten fazla parametre alan bir fonksiyonda çağrı satırı okunmaz hale gelir: sıra
ezberlenmelidir. Çözüm, seçenekleri bir nesnede toplayıp parametre listesinde
**yapıbozumla (destructuring)** açmaktır:

```js
function ozet({ istasyon, sicaklik, birim = "C" }) {
  return `${istasyon}: ${sicaklik} ${birim}`;
}
console.log(ozet({ istasyon: "A1", sicaklik: 21.5 }));
console.log(ozet({ istasyon: "A2", sicaklik: 19.75, birim: "K" }));
```

```
A1: 21.5 C
A2: 19.75 K
```

Çağrı satırı artık hangi değerin ne olduğunu kendisi söylüyor ve sıra önemsiz hale
geldi. Varsayılanlar tek tek alanlara verilebiliyor.

Bu yazımın bir tuzağı vardır: argüman hiç verilmezse yapıbozum `undefined` üzerinde
çalışmaya kalkar:

```js
function ozet({ istasyon, sicaklik }) {
  return `${istasyon}: ${sicaklik}`;
}
try {
  console.log(ozet());
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError: Cannot destructure property 'istasyon' of 'undefined' as it is undefined.
```

Çözüm, parametrenin tamamına boş nesne varsayılanı vermektir:

```js
function ozetGuvenli({ istasyon = "?", sicaklik = null } = {}) {
  return `${istasyon}: ${sicaklik}`;
}
console.log(ozetGuvenli());
console.log(ozetGuvenli({ istasyon: "B1" }));
```

```
?: null
B1: null
```

İki varsayılan katmanı birlikte çalışıyor: dıştaki `= {}` nesnenin kendisi eksikse,
içtekiler tek tek alanlar eksikse devreye giriyor.

Fonksiyonun tüm seçenekleri zorunluysa dıştaki varsayılan yazılmaz; o zaman eksik çağrı
hata verir ve bu istenen davranıştır. Seçim, sözleşmenin ne olduğuna bağlıdır.

#### Özet

- Eksik argüman `undefined` olur, fazla argüman yok sayılır; ikisi de hata üretmez.
- Varsayılan değer yalnızca argüman `undefined` iken devreye girer; `null` ve `0`
  varsayılanı tetiklemez.
- Varsayılan ifadeler her çağrıda ve yalnızca gerektiğinde değerlendirilir; kendilerinden
  önceki parametreleri kullanabilirler.
- Kalan parametre argümanları gerçek bir dizide toplar ve son sırada bulunur; çağrıdaki
  üç nokta ters yönde, diziyi argümanlara dağıtır.
- `arguments` nesnesi dizi değildir ve varsayılanla dolan parametreleri saymaz; kursta
  kalan parametre kullanılır.
- Yapıbozumlu parametre adlandırılmış seçenek sağlar; nesnenin tamamına varsayılan
  verilmezse argümansız çağrı hata verir.

#### Sonraki Adım

Bu derste geçen `(d) => d > esik` gibi kısa yazımlar açıklanmadan kullanıldı. Sonraki
ders ok fonksiyonlarını ele alacak: kısa sözdiziminin biçimleri, örtük dönüş kuralı ve
bu fonksiyonların `arguments` nesnesi ile bağlam davranışındaki farkı. Fark yalnızca
kısalık değildir; ok fonksiyonu bazı yerlerde kullanılamaz.

### Ok Fonksiyonları

Source: https://academia.sh/tr/kurslar/javascript-temelleri/ok-fonksiyonlari

Kısa sözdiziminin biçimleri, örtük dönüş ve nesne döndürme tuzağı, argüman nesnesinin bulunmaması ve bağlamın çevreleyen kapsamdan gelmesi.

Önceki derslerde `(d) => d > esik` gibi yazımlar açıklanmadan kullanıldı. Bu ders o
sözdizimini tanımlar. Ok fonksiyonu üçüncü bir fonksiyon tanımlama biçimidir ve iki
kazanım sunar: kısalık ve bağlam davranışının değişmesi.

İkincisi asıl olandır. Kısalık okunaklılığı bazen artırır bazen azaltır; bağlam
davranışı ise ok fonksiyonunun bazı yerlerde kullanılamamasına yol açar. Bu dersin
sonunda hangi biçimin nerede seçileceği bir kurala bağlanacak.

#### Sözdizimi Biçimleri

Ok fonksiyonu, parametre listesi ve gövdeden oluşur; ikisi `=>` ile ayrılır.

```js
const kare = (x) => x * x;
const tekParam = x => x * 2;
const iki = (a, b) => a + b;
const sifir = () => 42;
const govdeli = (dizi) => {
  let t = 0;
  for (const d of dizi) t += d;
  return t;
};

console.log(kare(4), tekParam(4), iki(1, 2), sifir());
console.log(govdeli([21.5, 19.75]));
```

```
16 8 3 42
41.25
```

Tek parametrede parantez atlanabilir; parametresiz ve çok parametreli biçimlerde
zorunludur. Kursta parantez her zaman yazılacaktır — tek biçimlilik, parametre eklendiğinde
yapılacak değişikliği azaltır.

Gövde iki türlüdür. Süslü ayraçsız gövde bir **ifadedir** ve değeri örtük olarak
döndürülür; `return` yazılmaz. Süslü ayraçlı gövde bir **bloktur** ve dönüş açıkça
yazılır. `govdeli` fonksiyonunda `return` olmasaydı sonuç `undefined` olurdu.

#### Nesne Döndürme Tuzağı

Örtük dönüşle nesne döndürmek, sözdiziminde bir belirsizlik yaratır: açılan süslü ayraç
nesne mi, blok mu?

```js
const hatali = (ad) => { istasyon: ad };
console.log(hatali("A1"));
```

```
undefined
```

Ayrıştırıcı süslü ayracı blok olarak okudu. Blok içindeki `istasyon: ad` satırı bir
nesne özelliği değil, `istasyon` etiketli bir deyimdir — Döngüler dersindeki etiketli
döngü sözdiziminin aynısı. Blokta `return` bulunmadığı için sonuç `undefined` oldu. Hata
verilmedi.

Çözüm, nesneyi parantez içine almaktır:

```js
const nesneDonen = (ad) => ({ istasyon: ad });
console.log(nesneDonen("A1"));
```

```
{ istasyon: 'A1' }
```

Parantez, süslü ayracın bir ifade bağlamında olduğunu belirtir; ayrıştırıcı artık nesne
okur.

#### Argüman Nesnesi Bulunmaz

Ok fonksiyonlarının kendi `arguments` nesnesi yoktur:

```js
const okFn = () => {
  try {
    return arguments.length;
  } catch (hata) {
    return hata.name + ": " + hata.message;
  }
};
console.log(okFn(1, 2));
```

```
ReferenceError: arguments is not defined
```

İleti "bulunamadı" diyor; ok fonksiyonu böyle bir ad tanımlamıyor. Değişken sayıda
argüman kalan parametreyle alınır:

```js
const kalanIle = (...args) => args.length;
console.log(kalanIle(1, 2));
```

```
2
```

Parametreler dersinde konan kural burada zorunlu hale geliyor.

#### Bağlam Çevreleyen Kapsamdan Gelir

Ok fonksiyonlarının kendi `this` bağlaması yoktur. Gövdedeki `this`, fonksiyonun
**tanımlandığı yerdeki** `this` değeridir. Adlandırma ve Kapsam Kuralları dersinde
kurulan sözcüksel kapsam ilkesi, ok fonksiyonlarında bağlama da uygulanır.

`this` bağlanmasının kuralları bir sonraki kursun konusudur. Burada yalnızca gözlemlenen
fark ele alınıyor ve bu fark en çok geri çağrı fonksiyonlarında görülür:

```js
const izleyici = {
  esik: 20,
  sicakliklar: [21.5, 19.75, 23],

  klasikGeriCagri() {
    try {
      return this.sicakliklar.filter(function (d) {
        return d > this.esik;
      });
    } catch (hata) {
      return hata.name + ": " + hata.message;
    }
  },

  okGeriCagri() {
    return this.sicakliklar.filter((d) => d > this.esik);
  },
};

console.log(izleyici.klasikGeriCagri());
console.log(izleyici.okGeriCagri());
```

```
TypeError: Cannot read properties of undefined (reading 'esik')
[ 21.5, 23 ]
```

İki yöntem de aynı nesnenin üzerinden çağrıldı ve dış `this` her ikisinde de nesnenin
kendisiydi. Fark iç fonksiyondadır. Klasik fonksiyon ifadesi kendi `this` bağlamasını
kurar; katı kipte bu bağlama `undefined` olur ve özellik okuma hata verir. Ok fonksiyonu
kendi bağlamasını kurmaz, dıştakini kullanır ve süzgeç beklendiği gibi çalışır.

Aynı davranış ters yönde de sonuç doğurur. Ok fonksiyonu bir nesne yöntemi olarak
yazıldığında, `this` nesneye bağlanmaz:

```js
const istasyon = {
  ad: "A1",
  klasikYontem() {
    return this === undefined ? "this: undefined" : `this.ad: ${this.ad}`;
  },
  okYontem: () => {
    return this === undefined ? "this: undefined" : `this.ad: ${this.ad}`;
  },
};
console.log(istasyon.klasikYontem());
console.log(istasyon.okYontem());
```

```
this.ad: A1
this: undefined
```

Ok fonksiyonu, tanımlandığı yerin `this` değerini aldı. Bu örnek bir modül dosyasında
çalıştırıldığı için en dış `this` değeri `undefined`'dır — Kod Çalıştırma Yolları
dersinde gösterilen modül kuralı. Betik kipinde aynı satır farklı bir değer verirdi; her
iki durumda da nesnenin kendisi olmazdı.

Kural buradan çıkar: **nesne yöntemleri ok fonksiyonuyla yazılmaz.**

#### Yapıcı Olarak Kullanılamaz

Ok fonksiyonları `new` ile çağrılamaz:

```js
const Olcum = (ad) => { this.ad = ad; };
try {
  new Olcum("A1");
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}

function KlasikOlcum(ad) { this.ad = ad; }
console.log(new KlasikOlcum("A1").ad);
console.log(typeof Olcum.prototype, typeof KlasikOlcum.prototype);
```

```
TypeError: Olcum is not a constructor
A1
undefined object
```

Son satır nedeni gösteriyor: ok fonksiyonunun `prototype` özelliği yoktur. Yapıcı
çağrısının bu özelliğe neden ihtiyaç duyduğu ve prototip zincirinin nasıl kurulduğu bir
sonraki kursun konusudur.

#### Biçim Seçimi

Üç tanım biçimi arasındaki seçim şu kurallara bağlanır:

- **Bildirim:** en dış kapsamdaki adlandırılmış yardımcı fonksiyonlar. Yukarı taşındığı
  için dosyadaki sıra serbesttir.
- **Ok fonksiyonu:** başka bir fonksiyona geçirilen kısa geri çağrılar ve çevreleyen
  bağlamı kullanması gereken iç fonksiyonlar.
- **Klasik fonksiyon ifadesi:** nesne yöntemleri ve `this` bağlamasının çağrı yerine göre
  belirlenmesi istenen durumlar.

Ok fonksiyonu her yerde kısa değildir. Gövdesi birkaç satırı geçen ve tek başına anlam
taşıyan bir işlem, adlandırılmış bir bildirime çıkarıldığında hem okunur hem yeniden
kullanılabilir olur.

#### Ölçüm Betiğine Uygulama

Dönüşüm zincirleri ok fonksiyonlarının en uygun kullanım alanıdır:

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5 },
  { istasyon: "A2", sicaklik: NaN },
  { istasyon: "B1", sicaklik: 23 },
  { istasyon: "B2", sicaklik: 18.25 },
];

const ESIK = 20;

const gecerli = (kayit) => !Number.isNaN(kayit.sicaklik);
const esikUstu = (kayit) => kayit.sicaklik > ESIK;
const etiketle = (kayit) => `${kayit.istasyon}=${kayit.sicaklik}`;

console.log(kayitlar.filter(gecerli).filter(esikUstu).map(etiketle));

console.log(
  kayitlar
    .filter((k) => !Number.isNaN(k.sicaklik))
    .map((k) => ({ ...k, sapma: k.sicaklik - ESIK })),
);
```

```
[ 'A1=21.5', 'B1=23' ]
[
  { istasyon: 'A1', sicaklik: 21.5, sapma: 1.5 },
  { istasyon: 'B1', sicaklik: 23, sapma: 3 },
  { istasyon: 'B2', sicaklik: 18.25, sapma: -1.75 }
]
```

İki yazım da aynı ailedendir. Birincide süzgeçler adlandırıldı ve zincir okunur kaldı;
ikincide kısa geri çağrılar yerinde yazıldı. İkinci çıktıda nesne döndürüldüğü için
parantez zorunluydu — bu dersin ikinci bölümündeki tuzak.

İkinci çıktıdaki çok satırlı gösterim, çalışma zamanının inceleme biçimidir; dizi belirli
bir uzunluğu aştığında satırlara bölünür. Bu biçim standartta tanımlı değildir ve
ortamlar arasında değişir.

Nesne yayma sözdizimi (`{ ...k }`) burada kullanıldı ve Nesneler dersinde tanımlanacak.

#### Özet

- Ok fonksiyonunda tek parametrede parantez atlanabilir; süslü ayraçsız gövde ifadedir
  ve değeri örtük döndürülür.
- Örtük dönüşle nesne döndürmek için nesne parantez içine alınır; alınmazsa süslü ayraç
  blok olarak ayrıştırılır ve sonuç `undefined` olur.
- Ok fonksiyonlarının `arguments` nesnesi yoktur; değişken argüman kalan parametreyle
  alınır.
- Ok fonksiyonunun `this` değeri tanımlandığı kapsamdan gelir; bu, geri çağrılarda
  istenen davranıştır; nesne yöntemlerinde istenmeyen davranıştır.
- Ok fonksiyonları `new` ile çağrılamaz; `prototype` özellikleri yoktur.

#### Sonraki Adım

Bu konudaki örneklerde hata durumları `try`/`catch` ile yakalandı ama yapı hiç
tanımlanmadı. Sonraki ders hata yönetimini ele alacak: `try`/`catch`/`finally` bloklarının
akışı, `throw` deyimi, yerleşik hata tipleri ve kendi hata tipini tanımlama. Ölçüm
betiğinin bozuk veriye verdiği yanıt da orada bir sözleşmeye bağlanacak.

### Hata Yönetimi

Source: https://academia.sh/tr/kurslar/javascript-temelleri/hata-yonetimi

`try`/`catch`/`finally` akışı, `throw` deyimi, yerleşik hata tipleri, hata nesnesinin alanları, neden zinciri ve hata ile dönüş değeri arasındaki seçim.

Önceki derslerde `try`/`catch` yapısı hata iletilerini göstermek için kullanıldı ama
tanımlanmadı. Bu ders yapıyı tamamlar: blokların akışı, hata fırlatma, yerleşik hata
tipleri ve — en önemlisi — bir sorunun hata mı yoksa dönüş değeri mi olarak
bildirileceği kararı.

Kod Çalıştırma Yolları dersinde hata nesnesinin ad, ileti ve yığıt izi taşıdığı
gösterilmişti. Burada bu nesneleri kendimiz üreteceğiz.

#### Blokların Akışı

`try` bloğu izlenen kodu içerir. Hata oluşursa akış `catch` bloğuna atlar. `finally`
bloğu, hata olsun olmasın çalışır.

```js
function dene(deger) {
  try {
    console.log("1 try basi");
    if (deger < 0) throw new Error("negatif deger");
    console.log("2 try sonu");
    return "try donusu";
  } catch (hata) {
    console.log("3 catch:", hata.message);
    return "catch donusu";
  } finally {
    console.log("4 finally");
  }
}
console.log(dene(1));
console.log("---");
console.log(dene(-1));
```

```
1 try basi
2 try sonu
4 finally
try donusu
---
1 try basi
3 catch: negatif deger
4 finally
catch donusu
```

İki gözlem önemlidir. Birincisi, hata oluştuğunda `try` bloğunun kalan satırları
çalışmaz; akış doğrudan `catch`'e geçer. İkincisi, `finally` bloğu `return` deyiminden
**sonra** ama fonksiyon gerçekten dönmeden **önce** çalışır. Bu yüzden temizlik işleri
oraya yazılır: açılan bir kaynağın kapatılması, ayrılan bir kilidin bırakılması.

`finally` içinde `return` yazmak, dönüş değerini ezer:

```js
function ezer() {
  try {
    return "try";
  } finally {
    return "finally";
  }
}
console.log(ezer());
```

```
finally
```

Aynı davranış fırlatılan hatayı da yutar. Bu yüzden `finally` bloğu yalnızca temizlik
içerir; akışı yönlendiren bir deyim taşımaz.

Hata nesnesine ihtiyaç yoksa yakalama bağı yazılmayabilir:

```js
function gecerliJson(metin) {
  try {
    JSON.parse(metin);
    return true;
  } catch {
    return false;
  }
}
console.log(gecerliJson('{"a":1}'), gecerliJson("{bozuk}"));
```

```
true false
```

#### Yerleşik Hata Tipleri

Standart birkaç hata tipi tanımlar ve dilin kendi ürettiği hatalar bunlardan biridir:

```js
const denemeler = [
  () => null.x,
  () => bilinmeyenAd,
  () => ([].length = -1),
  () => (1234).toFixed(200),
];
for (const d of denemeler) {
  try {
    d();
  } catch (hata) {
    console.log(hata.name + ": " + hata.message);
  }
}
```

```
TypeError: Cannot read properties of null (reading 'x')
ReferenceError: bilinmeyenAd is not defined
RangeError: Invalid array length
RangeError: toFixed() digits argument must be between 0 and 100
```

`TypeError` bir değerin beklenen türde olmadığını, `ReferenceError` bir adın
bulunamadığını, `RangeError` bir değerin izin verilen aralığın dışında olduğunu bildirir.
`SyntaxError` ise ayrıştırma sırasında üretilir; ilk derste görüldüğü gibi programı hiç
başlatmaz — tek istisnası, metin ayrıştıran yerleşik yöntemlerin çalışma zamanında
ürettiği durumdur.

Hata **adları** standarttır; **iletiler** değildir. İleti metinleri çalışma zamanları
arasında ve sürümler arasında değişir. Bu yüzden kodda ileti metnine göre dallanılmaz;
ada, tipe veya kendi eklediğimiz bir alana bakılır.

#### Hata Fırlatmak

`throw` deyimi herhangi bir değeri fırlatabilir. Fırlatılan şeyin hata nesnesi olması
zorunlu değildir, ama olmalıdır:

```js
try {
  throw "duz metin";
} catch (hata) {
  console.log(typeof hata, hata);
  console.log("name:", hata.name, "| stack:", typeof hata.stack);
}

try {
  throw new Error("hata nesnesi");
} catch (hata) {
  console.log(typeof hata, hata instanceof Error);
  console.log("name:", hata.name, "| stack:", typeof hata.stack);
}
```

```
string duz metin
name: undefined | stack: undefined
object true
name: Error | stack: string
```

Metin fırlatıldığında ad ve yığıt izi yoktur; hatanın nereden geldiği kaybolur. Kursun
kuralı: **fırlatılan her değer bir hata nesnesidir.**

Alt katmandaki bir hatayı sararken özgün hata korunmalıdır. Hata nesnesi bunun için bir
**neden (cause)** alanı taşıyabilir:

```js
function sicaklikCevir(metin) {
  const deger = Number(metin);
  if (Number.isNaN(deger)) {
    throw new Error(`sayiya cevrilemedi: ${JSON.stringify(metin)}`);
  }
  return deger;
}

function kaydiOku(satir) {
  const [istasyon, sicaklikMetni] = satir.split(";");
  try {
    return { istasyon, sicaklik: sicaklikCevir(sicaklikMetni) };
  } catch (hata) {
    throw new Error(`kayit okunamadi: ${istasyon}`, { cause: hata });
  }
}

console.log(kaydiOku("A1;21.5"));
try {
  kaydiOku("B2;abc");
} catch (hata) {
  console.log(hata.message);
  console.log("neden:", hata.cause.message);
}
```

```
{ istasyon: 'A1', sicaklik: 21.5 }
kayit okunamadi: B2
neden: sayiya cevrilemedi: "abc"
```

Üst katman kendi bağlamını ekledi (hangi kayıt); alt katmanın bilgisi (neden) kayboldu
sayılmadı. Bu, hata iletilerini bağlamla zenginleştirmenin standart yoludur.

#### Kendi Hata Tipini İşaretlemek

Bir hatayı kendi ürettiğimizi ayırt edebilmek gerekir; aksi halde `catch` bloğu, kodun
kendi hatalarıyla birlikte beklenmedik programlama hatalarını da yutar.

En yalın yol, hata nesnesine ad ve bir kod alanı eklemektir:

```js
function olcumHatasi(kod, ileti) {
  const hata = new Error(ileti);
  hata.name = "OlcumHatasi";
  hata.kod = kod;
  return hata;
}

const h = olcumHatasi("BOS_ALAN", "sicaklik alani bos");
console.log(h.name, "|", h.kod, "|", h.message, "|", h instanceof Error);
```

```
OlcumHatasi | BOS_ALAN | sicaklik alani bos | true
```

Kalıtımla adlandırılmış hata sınıfları tanımlamak — sınıf sözdizimi kullanarak — bir
sonraki kursun konusudur. Bu kursta ad ve kod alanı yeterlidir.

#### Ölçüm Betiğine Uygulama

Bozuk veriye verilen yanıt artık bir sözleşmeye bağlanabilir: her satır ya kabul edilir
ya da gerekçesiyle reddedilir; hiçbir satır sessizce yanlış değere dönüşmez.

```js
const KOD_BOS = "BOS_ALAN";
const KOD_SAYI = "SAYI_DEGIL";

function olcumHatasi(kod, ileti) {
  const hata = new Error(ileti);
  hata.name = "OlcumHatasi";
  hata.kod = kod;
  return hata;
}

function sicaklikOku(metin) {
  if (metin.trim() === "") {
    throw olcumHatasi(KOD_BOS, "sicaklik alani bos");
  }
  const deger = Number(metin);
  if (Number.isNaN(deger)) {
    throw olcumHatasi(KOD_SAYI, `sicaklik sayiya cevrilemedi: ${JSON.stringify(metin)}`);
  }
  return deger;
}

const satirlar = ["A1;21.5", "A2;", "B1;abc", "B2;0"];
const kabul = [];
const red = [];

for (const satir of satirlar) {
  const [istasyon, alan] = satir.split(";");
  try {
    kabul.push({ istasyon, sicaklik: sicaklikOku(alan) });
  } catch (hata) {
    if (hata.name !== "OlcumHatasi") throw hata;
    red.push({ istasyon, kod: hata.kod, ileti: hata.message });
  }
}

console.log("kabul:", kabul);
console.log("red:", red);
```

```
kabul: [ { istasyon: 'A1', sicaklik: 21.5 }, { istasyon: 'B2', sicaklik: 0 } ]
red: [
  { istasyon: 'A2', kod: 'BOS_ALAN', ileti: 'sicaklik alani bos' },
  {
    istasyon: 'B1',
    kod: 'SAYI_DEGIL',
    ileti: 'sicaklik sayiya cevrilemedi: "abc"'
  }
]
```

Üç karar bu betiği önceki sürümlerinden ayırıyor.

Birincisi, `B2` kaydındaki `0` değeri kabul edildi. Doğruluk Değerleri dersinde kurulan
ayrım korunuyor: sıfır geçerli bir ölçümdür.

İkincisi, yakalama bloğu **kendi hatasını tanıyor**. `hata.name !== "OlcumHatasi"`
denetimi, beklenmedik bir hatayı yeniden fırlatıyor. Bu satır olmasaydı, örneğin
`satir.split` çağrısında oluşacak bir `TypeError` de "reddedilen kayıt" sayılır ve
programlama hatası veri hatası gibi görünürdü.

Üçüncüsü, red listesi gerekçe taşıyor. Kaç kaydın neden reddedildiği çıktıdan
okunabiliyor.

#### Hata mı, Dönüş Değeri mi

Her olağandışı durum hata değildir. Ölçüt şudur: **çağıran taraf durumu öngörebiliyorsa
dönüş değeri, öngöremiyorsa hata uygundur.**

Boş bir listenin ortalamasını istemek öngörülebilir bir durumdur; Fonksiyon Bildirimi ve
İfadesi dersindeki `ortalama` fonksiyonu bu yüzden `null` döndürdü. Buna karşılık,
sayısal olması gereken bir alanda harf bulunması verinin sözleşmesinin ihlalidir; hata
uygundur.

İki uygulama da yukarıdaki betikte bir arada bulunuyor: `sicaklikOku` hata fırlatıyor,
çağıran döngü hatayı yakalayıp bir veri yapısına — red listesine — çeviriyor. Sınır,
sorunun nerede ele alınabileceğine göre çizilir.

Hatalar akış denetimi için kullanılmaz. Bir döngüden çıkmak, bir değeri atlamak veya bir
koşulu bildirmek için hata fırlatmak, hem maliyetli hem de okunması güç bir kalıptır;
`break`, `continue` ve dönüş değerleri bu iş içindir.

#### Özet

- `try` bloğunda hata oluşursa kalan satırlar çalışmaz; `finally` her durumda ve `return`
  değerlendirildikten sonra çalışır.
- `finally` içinde `return` veya `throw` yazmak, dönüş değerini ve hatayı ezer; blok
  yalnızca temizlik içerir.
- Hata adları standarttır, iletiler değildir; dallanma ileti metnine göre yapılmaz.
- Fırlatılan her değer hata nesnesi olmalıdır; aksi halde ad ve yığıt izi bulunmaz.
- Sarmalanan hatalar `cause` alanıyla korunur; kendi hataları ad ve kod alanıyla
  işaretlenir ve tanınmayan hatalar yeniden fırlatılır.
- Öngörülebilir durum dönüş değeriyle, sözleşme ihlali hatayla bildirilir.

#### Sonraki Adım

Denetim akışı ve fonksiyonlar tamamlandı. Bu konudaki örneklerde `filter`, `map`, `split`
ve nesne yayma gibi yapılar açıklanmadan kullanıldı. Sonraki konu bunları tanımlayacak:
dizilerin oluşturma ve dönüştürme yöntemleri, nesne erişim kısayolları, şablon dizgileri,
JSON dönüşümleri ve tarih–zaman gösterimi. Ölçüm listesi orada metinden okunup metne geri
yazılan tam bir veri kümesine dönüşecek.

## Yerleşik Veri Yapıları

Dizi ve nesne işlemleri, dizgi biçimlendirme, JSON ile dizileştirme ve ayrıştırma, tarih–zaman gösterimi.

### Diziler

Source: https://academia.sh/tr/kurslar/javascript-temelleri/diziler

Dizi oluşturma yolları ve boşluklar, dizin erişimi, değiştiren ve değiştirmeyen yöntemlerin ayrımı, sıralama tuzağı ve dönüştürme zincirleri.

Önceki konuda `filter`, `map` ve `split` gibi yöntemler açıklanmadan kullanıldı. Bu konu
onları tanımlıyor. Veri Yapıları kursunda dizinin iç yapısı, maliyeti ve dinamik dizinin
amortize davranışı incelenmişti; burada JavaScript'in dizi nesnesinin somut davranışı ve
tuzakları ele alınacak.

JavaScript dizisi, Veri Yapıları kursundaki bitişik bellek modelinin birebir karşılığı
değildir. `typeof` sonucu `object`'tir; dizinleri anahtar olarak taşıyan, `length`
özelliği kendini güncelleyen özel bir nesnedir. Bu ayrım birkaç davranışı doğrudan
açıklar.

#### Oluşturma

```js
const dogrudan = [21.5, 19.75, 23];
const bosluklu = new Array(3);
const tekEleman = Array.of(3);
const metinden = Array.from("A1");
const uretilen = Array.from({ length: 3 }, (_, i) => i * 10);

console.log(dogrudan, dogrudan.length);
console.log(bosluklu, bosluklu.length);
console.log(tekEleman, metinden);
console.log(uretilen);
console.log(Array.isArray(dogrudan), typeof dogrudan);
```

```
[ 21.5, 19.75, 23 ] 3
[ <3 empty items> ] 3
[ 3 ] [ 'A', '1' ]
[ 0, 10, 20 ]
true object
```

İkinci satır bir tuzağı gösteriyor: tek sayısal argümanla çağrılan yapıcı, o değeri
eleman yapmaz, **o uzunlukta boş bir dizi** üretir. `Array.of` bu belirsizliği ortadan
kaldırır ve argümanı eleman sayar.

`Array.from` iki iş yapar: yinelenebilir bir değeri diziye çevirir ve `length` alanı olan
bir nesneden dizi üretir. İkinci argümanı, her eleman için çağrılan bir eşleme
fonksiyonudur. Alt çizgi adı, kullanılmayan parametre için seçilmiş bir yazım
sözleşmesidir; dilin kuralı değildir.

Son satır tip gözlemidir: bir değerin dizi olup olmadığı `typeof` ile anlaşılmaz,
`Array.isArray` ile sınanır.

#### Boşluklar

Bir dizinin bazı dizinlerinde eleman hiç bulunmayabilir. Bu **boşluklar (holes)**,
`undefined` değeri taşıyan elemanlardan farklıdır:

```js
const bosluklu = new Array(3);
console.log(bosluklu[0], 0 in bosluklu);
console.log(bosluklu.map(() => 1));
console.log(Array.from({ length: 3 }, () => 1));
```

```
undefined false
[ <3 empty items> ]
[ 1, 1, 1 ]
```

İlk satır ayrımı gösteriyor: okuma `undefined` verdi, ama dizin dizide **yok**. İkinci
satır sonucudur: eşleme yöntemi boşlukları atlar ve geri çağrı hiç çalışmaz. Üçüncü satır
doğru yoldur.

Boşluklar dizine doğrudan yazarak da oluşur:

```js
const dizi = [21.5, 19.75];
dizi.length = 1;
console.log(dizi);
dizi[4] = 99;
console.log(dizi, dizi.length);
```

```
[ 21.5 ]
[ 21.5, <3 empty items>, 99 ] 5
```

`length` alanına yazmak diziyi kırpar. Uzunluğun ötesindeki bir dizine yazmak ise
aradaki dizinleri boş bırakır ve `length` değerini büyütür. Kursun kuralı: **diziler
boşluklu bırakılmaz**; ekleme `push` ile, üretme `Array.from` ile yapılır.

Çıktıdaki `<3 empty items>` gösterimi çalışma zamanının inceleme biçimidir; boşluğu
`undefined` değerinden ayırt etmek için kullanılır ve ortamlar arasında değişir.

#### Erişim ve Arama

```js
const s = [21.5, 19.75, 23, 18.25];
console.log(s[0], s[s.length - 1], s.at(-1), s.at(-2));
console.log(s[10]);
console.log(s.indexOf(23), s.includes(23));

const nanli = [NaN, 1];
console.log(nanli.indexOf(NaN), nanli.includes(NaN));
```

```
21.5 18.25 18.25 23
undefined
2 true
-1 true
```

Aralık dışı dizin hata vermez, `undefined` verir. `at` yöntemi negatif dizin kabul eder
ve sondan sayar.

Son satır, Eşitlik Karşılaştırmaları dersinde bırakılan bir ipucunun karşılığıdır:
`indexOf` katı eşitlik kullanır ve `NaN` hiçbir şeye eşit olmadığı için bulunamaz;
`includes` ise değer eşitliğine yakın bir bağıntı kullanır ve `NaN` bulur. İki yöntem
aynı soruyu sormaz.

#### Değiştiren ve Değiştirmeyen Yöntemler

Diziler değiştirilebilir nesnelerdir. Yöntemler iki sınıfa ayrılır ve hangi sınıfta
olduğunu bilmemek, paylaşılan veride sessiz hatalar üretir.

**Değiştiren yöntemler** diziyi yerinde günceller: `push`, `pop`, `shift`, `unshift`,
`splice`, `sort`, `reverse`, `fill`.

**Değiştirmeyen yöntemler** yeni bir dizi döndürür: `slice`, `concat`, `map`, `filter`,
`flat`, `toSorted`, `toReversed`.

```js
const asil = [21.5, 19.75, 23];

const kopya = asil.slice();
kopya.push(18.25);
console.log(asil, kopya);

const ters = asil.toReversed();
console.log(asil, ters);

const yerinde = [...asil];
yerinde.reverse();
console.log(yerinde);
```

```
[ 21.5, 19.75, 23 ] [ 21.5, 19.75, 23, 18.25 ]
[ 21.5, 19.75, 23 ] [ 23, 19.75, 21.5 ]
[ 23, 19.75, 21.5 ]
```

Yayma sözdizimiyle (`[...asil]`) veya `slice` ile alınan kopya **sığdır**: dizinin
kendisi yenidir, ama elemanlar aynı nesnelerdir. Programlama Temelleri kursundaki sığ
kopya–derin kopya ayrımı burada geçerlidir; nesne dizilerinde eleman üzerinde yapılan
değişiklik her iki diziden de görülür.

#### Sıralama Tuzağı

`sort` yöntemi, karşılaştırma fonksiyonu verilmediğinde elemanları **metne çevirip
sözlük sırasına göre** sıralar:

```js
const sayilar = [21.5, 19.75, 100, 3];
console.log([...sayilar].sort());
console.log([...sayilar].sort((a, b) => a - b));
console.log(sayilar.toSorted((a, b) => b - a));
console.log(sayilar);
```

```
[ 100, 19.75, 21.5, 3 ]
[ 3, 19.75, 21.5, 100 ]
[ 100, 21.5, 19.75, 3 ]
[ 21.5, 19.75, 100, 3 ]
```

İlk satır sayısal olarak yanlıştır ama kural gereği doğrudur: `"100"` metni `"3"`
metninden önce gelir. Sayısal sıralama için karşılaştırma fonksiyonu zorunludur.
Fonksiyon negatif, sıfır veya pozitif döndürerek sırayı bildirir; `a - b` artan, `b - a`
azalan sıra verir.

Son iki satır ayrıca değiştiren–değiştirmeyen ayrımını gösteriyor: `toSorted` özgün
diziyi bozmadı. `sort` çağrıları kopya üzerinde yapıldığı için `sayilar` hiç değişmedi.

#### Dönüştürme Zincirleri

Ölçüm listesi üzerindeki işlerin çoğu birkaç yöntemin zincirlenmesiyle yazılır:

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5 },
  { istasyon: "A2", sicaklik: 19.75 },
  { istasyon: "B1", sicaklik: 23 },
  { istasyon: "B2", sicaklik: 18.25 },
];

console.log(kayitlar.map((k) => k.sicaklik));
console.log(kayitlar.filter((k) => k.sicaklik > 20).map((k) => k.istasyon));
console.log(kayitlar.reduce((t, k) => t + k.sicaklik, 0));
console.log(kayitlar.find((k) => k.istasyon === "B1"));
console.log(kayitlar.findIndex((k) => k.sicaklik > 22));
console.log(kayitlar.some((k) => k.sicaklik > 22), kayitlar.every((k) => k.sicaklik > 15));
```

```
[ 21.5, 19.75, 23, 18.25 ]
[ 'A1', 'B1' ]
82.5
{ istasyon: 'B1', sicaklik: 23 }
2
true true
```

Programlama Temelleri kursundaki eşleme, süzme ve indirgeme üçlüsü bunlardır. `find` ilk
eşleşen elemanı, `findIndex` dizinini verir; bulunamazsa sırasıyla `undefined` ve `-1`
döner. `some` ve `every` mantıksal değer üretir ve kısa devre yapar.

İndirgeme, birikeci bir nesne olduğunda tek geçişte birden çok özet üretir:

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5 },
  { istasyon: "A2", sicaklik: 19.75 },
  { istasyon: "B1", sicaklik: 23 },
];

const ozet = kayitlar.reduce(
  (birikec, kayit) => ({
    adet: birikec.adet + 1,
    toplam: birikec.toplam + kayit.sicaklik,
    enYuksek: Math.max(birikec.enYuksek, kayit.sicaklik),
  }),
  { adet: 0, toplam: 0, enYuksek: -Infinity },
);
console.log(ozet);
console.log("ortalama:", ozet.toplam / ozet.adet);
```

```
{ adet: 3, toplam: 64.25, enYuksek: 23 }
ortalama: 21.416666666666668
```

Başlangıç değeri isteğe bağlı görünür ama değildir:

```js
try {
  console.log([].reduce((a, b) => a + b));
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
console.log([].reduce((a, b) => a + b, 0));
```

```
TypeError: Reduce of empty array with no initial value
0
```

Başlangıç değeri verilmezse ilk eleman birikeç olur ve boş dizide böyle bir eleman
yoktur. Kural: **indirgeme her zaman başlangıç değeriyle yazılır.**

İç içe listeler `flat` ve `flatMap` ile düzleştirilir:

```js
const gruplar = [[21.5, 19.75], [23], [], [18.25]];
console.log(gruplar.flat());
console.log(gruplar.flatMap((g) => g.map((d) => d * 2)));
console.log([1, [2, [3, [4]]]].flat(2));
```

```
[ 21.5, 19.75, 23, 18.25 ]
[ 43, 39.5, 46, 36.5 ]
[ 1, 2, 3, [ 4 ] ]
```

`flat` öntanımlı olarak yalnızca bir düzey açar; derinlik argümanla verilir. Son satırda
iki düzey açıldı ve üçüncü düzey dizi olarak kaldı.

#### Özet

- Tek sayısal argümanlı dizi yapıcısı o uzunlukta boş dizi üretir; eleman için `Array.of`
  veya doğrudan yazım kullanılır.
- Boşluklar `undefined` taşıyan elemanlardan farklıdır ve eşleme yöntemleri tarafından
  atlanır; `length` alanına yazmak diziyi kırpar veya boşluk açar.
- Aralık dışı dizin `undefined` verir; `at` negatif dizin kabul eder; `indexOf` `NaN`
  bulamaz; `includes` bulur.
- Yöntemler değiştiren ve değiştirmeyen olarak ikiye ayrılır; kopyalar sığdır.
- Karşılaştırma fonksiyonu verilmeyen sıralama metne çevirip sözlük sırası uygular.
- İndirgeme başlangıç değeriyle yazılır; verilmezse boş dizide hata üretir.

#### Sonraki Adım

Bu derste kayıtlar nesne olarak kullanıldı ama nesne sözdizimi tanımlanmadı. Sonraki ders
nesneleri ele alacak: özellik erişiminin iki biçimi, kısaltmalı yazım, hesaplanmış
anahtarlar, yapıbozum ve yayma. Ölçüm kayıtlarını istasyona göre gruplamak da orada
yazılacak.

### Nesneler

Source: https://academia.sh/tr/kurslar/javascript-temelleri/nesneler

Özellik erişiminin iki biçimi, anahtar dizgileştirme, kısaltmalı ve hesaplanmış yazım, varlık denetimi, yapıbozum, yayma ve referans anlambilimi.

Önceki derste ölçüm kayıtları nesne olarak kullanıldı ama sözdizimi tanımlanmadı. Bu ders
nesneleri ele alıyor: oluşturma, erişim, ayırma ve birleştirme.

Nesne, anahtar–değer çiftlerinin sıralı bir kümesidir. Anahtarlar dizgi veya
semboldür; değerler herhangi bir tipte olabilir. Prototip zinciri, özellik tanımlayıcıları
ve sınıf sözdizimi bir sonraki kursun konusudur; bu ders yalın veri nesnelerini kapsar.

#### Oluşturma ve Erişim

```js
const alan = "sicaklik";
const istasyon = "A1";
const sicaklik = 21.5;

const kayit = {
  istasyon,
  sicaklik,
  [alan + "Birimi"]: "C",
  ozet() {
    return `${this.istasyon}: ${this.sicaklik}`;
  },
};

console.log(kayit);
console.log(kayit.istasyon, kayit["sicaklik"], kayit[alan]);
console.log(kayit.ozet());
```

```
{
  istasyon: 'A1',
  sicaklik: 21.5,
  sicaklikBirimi: 'C',
  ozet: [Function: ozet]
}
A1 21.5 21.5
A1: 21.5
```

Üç yazım kısayolu birlikte kullanıldı. **Kısaltmalı özellik** (`istasyon`), aynı adlı bir
değişkenden değer alır. **Hesaplanmış anahtar** (`[alan + "Birimi"]`), anahtarı çalışma
anında üretir. **Kısaltmalı yöntem** (`ozet()`), fonksiyon özelliğini `function`
sözcüğü olmadan yazar; Ok Fonksiyonları dersindeki kural gereği yöntemler ok biçiminde
yazılmaz.

Erişimin iki yazımı vardır. **Nokta biçimi** sabit ve geçerli bir ad gerektirir.
**Köşeli ayraç biçimi** herhangi bir ifade kabul eder; anahtar çalışma anında
belirleniyorsa zorunludur. Çıktının ikinci satırında üç erişim biçimi de aynı değeri
verdi.

Nesnenin çok satırlı yazılması çalışma zamanının inceleme biçimidir; nesne belirli bir
uzunluğu aştığında satırlara bölünür ve fonksiyon özellikleri `[Function: ad]` biçiminde
gösterilir. Bu gösterim standartta tanımlı değildir.

#### Anahtarlar Dizgidir

Sembol dışındaki her anahtar dizgiye çevrilir:

```js
const nesne = {};
nesne[1] = "sayi bir";
nesne["1"] = "metin bir";
nesne[true] = "mantiksal";
console.log(nesne);
console.log(Object.keys(nesne));
console.log(typeof Object.keys(nesne)[0]);
```

```
{ '1': 'metin bir', true: 'mantiksal' }
[ '1', 'true' ]
string
```

`1` ve `"1"` aynı anahtardır; ikinci atama birinciyi ezdi. Döngüler dersinde `for...in`
ile gelen dizinlerin dizgi olmasının nedeni budur — dizi de bir nesnedir ve dizinleri
anahtar olarak taşır.

Sayısal anahtarlı bir eşleme gerekiyorsa `Map` yapısı ayrımı korur; bu kursun kapsamı
dışındadır.

#### Varlık Denetimi

Bir alanın **bulunmaması** ile **`undefined` değeri taşıması** farklı durumlardır:

```js
const kayit = { istasyon: "A1", nem: undefined };
console.log(kayit.nem, "nem" in kayit, Object.hasOwn(kayit, "nem"));
console.log(kayit.basinc, "basinc" in kayit, Object.hasOwn(kayit, "basinc"));
delete kayit.nem;
console.log("silme sonrasi:", "nem" in kayit, kayit);
```

```
undefined true true
undefined false false
silme sonrasi: false { istasyon: 'A1' }
```

Okuma her iki durumda da `undefined` verdi; ayrımı yalnızca varlık denetimi gösterdi.
`in` işleci kalıtılan özellikleri de sayar; `Object.hasOwn` yalnızca nesnenin kendi
özelliklerine bakar. Kursta ikincisi kullanılacaktır.

`delete` özelliği tümüyle kaldırır. `undefined` atamak ise özelliği bırakır ve yalnızca
değerini değiştirir.

#### Anahtar–Değer Çevrimleri

Nesne ile dizi arasında gidip gelmek, dönüşüm yazmanın temelidir:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5, nem: 48 };
console.log(Object.keys(kayit));
console.log(Object.values(kayit));
console.log(Object.entries(kayit));

const ciftler = [["istasyon", "B1"], ["sicaklik", 23]];
console.log(Object.fromEntries(ciftler));
```

```
[ 'istasyon', 'sicaklik', 'nem' ]
[ 'A1', 21.5, 48 ]
[ [ 'istasyon', 'A1' ], [ 'sicaklik', 21.5 ], [ 'nem', 48 ] ]
{ istasyon: 'B1', sicaklik: 23 }
```

`Object.entries` ile `Object.fromEntries` birbirinin tersidir. Bu çift, nesne üzerinde
dizi yöntemleri kullanmayı mümkün kılar: nesne diziye çevrilir, süzülür veya eşlenir,
sonra nesneye geri çevrilir.

#### Yapıbozum

**Yapıbozum (destructuring)**, bir nesnenin veya dizinin alanlarını ayrı adlara ayırır:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5, konum: { yukseklik: 120 } };

const { istasyon, sicaklik: derece, nem = null } = kayit;
console.log(istasyon, derece, nem);

const { konum: { yukseklik } } = kayit;
console.log(yukseklik);

const { istasyon: ad, ...digerleri } = kayit;
console.log(ad, digerleri);

const [ilk, , ucuncu = 0] = [21.5, 19.75];
console.log(ilk, ucuncu);
```

```
A1 21.5 null
120
A1 { sicaklik: 21.5, konum: { yukseklik: 120 } }
21.5 0
```

Dört yetenek kullanıldı: **yeniden adlandırma** (`sicaklik: derece`), **varsayılan**
(`nem = null`), **iç içe ayırma** ve **kalan alanların toplanması**. Varsayılanın ölçütü
Parametreler dersindekiyle aynıdır: yalnızca değer `undefined` ise devreye girer.

Dizi yapıbozumunda sıra konuma göredir; atlanacak eleman boş bırakılır. Döngüler
dersinde `entries` ile kullanılan `[dizin, deger]` yazımı budur.

#### Yayma ve Birleştirme

Yayma sözdizimi, bir nesnenin kendi sayılabilir özelliklerini başka bir nesne
sözdiziminin içine kopyalar:

```js
const temel = { istasyon: "A1", sicaklik: 21.5, birim: "C" };
const guncel = { sicaklik: 22.75 };

console.log({ ...temel, ...guncel });
console.log({ ...guncel, ...temel });
console.log(temel);
```

```
{ istasyon: 'A1', sicaklik: 22.75, birim: 'C' }
{ sicaklik: 21.5, istasyon: 'A1', birim: 'C' }
{ istasyon: 'A1', sicaklik: 21.5, birim: 'C' }
```

**Sıra sonucu belirler:** sonra yazılan aynı adlı özellik öncekini ezer. İkinci satırda
sıra tersine çevrildiği için güncelleme kaybolmuştur. Üçüncü satır, özgün nesnenin
değişmediğini gösteriyor — yayma yeni bir nesne üretir.

Kopya **sığdır**:

```js
const derin = { istasyon: "A1", konum: { yukseklik: 120 } };
const kopya = { ...derin };
kopya.konum.yukseklik = 999;
console.log(derin.konum.yukseklik);
```

```
999
```

İç nesne kopyalanmadı; iki nesne aynı `konum` nesnesini paylaşıyor. Derin kopya bu
kursun kapsamı dışındadır ve Değişmezlik Teknikleri dersinde ele alınacaktır.

#### Referans Anlambilimi

Nesneler adlara referansla bağlanır. Programlama Temelleri kursundaki Değer ve Referans
ile Aktarım dersinde kurulan kural burada birebir geçerlidir:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5 };
const ikinciAd = kayit;
ikinciAd.sicaklik = 22.75;
console.log(kayit.sicaklik);

function bozar(k) { k.sicaklik = 0; }
function bozmaz(k) { return { ...k, sicaklik: 0 }; }

bozar(kayit);
console.log(kayit.sicaklik);
console.log(bozmaz(kayit), kayit.sicaklik);
```

```
22.75
0
{ istasyon: 'A1', sicaklik: 0 } 0
```

`bozar` fonksiyonu çağıranın verisini değiştirdi. `bozmaz` ise yeni bir nesne üretti ve
özgün kaydı olduğu gibi bıraktı — son satırdaki `0` değeri, `bozar` çağrısından kalmadır.

Kursun kuralı: **dönüşüm fonksiyonları girdilerini değiştirmez, yeni nesne döndürür.**
Yan etkili bir güncelleme gerekiyorsa fonksiyonun adı bunu söylemelidir.

#### Ölçüm Betiğine Uygulama

Kayıtları istasyona göre gruplamak, nesnenin eşleme olarak kullanımıdır:

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5 },
  { istasyon: "A2", sicaklik: 19.75 },
  { istasyon: "A1", sicaklik: 22.75 },
  { istasyon: "B1", sicaklik: 23 },
];

const gruplar = {};
for (const kayit of kayitlar) {
  const anahtar = kayit.istasyon;
  if (!Object.hasOwn(gruplar, anahtar)) {
    gruplar[anahtar] = [];
  }
  gruplar[anahtar].push(kayit.sicaklik);
}
console.log(gruplar);

const ortalamalar = Object.fromEntries(
  Object.entries(gruplar).map(([ad, degerler]) => [
    ad,
    degerler.reduce((t, d) => t + d, 0) / degerler.length,
  ]),
);
console.log(ortalamalar);
```

```
{ A1: [ 21.5, 22.75 ], A2: [ 19.75 ], B1: [ 23 ] }
{ A1: 22.125, A2: 19.75, B1: 23 }
```

İki adım da bu dersin araçlarını kullanıyor. Gruplama, hesaplanmış anahtarla nesneye
yazıyor ve varlık denetimini `Object.hasOwn` ile yapıyor. Ortalama hesabı, nesneyi
çiftlere çevirip eşleme uyguluyor ve nesneye geri döndürüyor. Geri çağrının parametresi
`[ad, degerler]` yazımıyla yapıbozuma uğratıldı.

#### Özet

- Kısaltmalı özellik, hesaplanmış anahtar ve kısaltmalı yöntem yazımları nesne
  sözdiziminin üç kısayoludur.
- Sembol dışındaki anahtarlar dizgiye çevrilir; `1` ile `"1"` aynı anahtardır.
- Alanın bulunmaması ile `undefined` taşıması ayrı durumlardır; ayrım `Object.hasOwn` ile
  görülür.
- `Object.entries` ve `Object.fromEntries` birbirinin tersidir ve nesne üzerinde dizi
  yöntemleri kullanmayı sağlar.
- Yapıbozum yeniden adlandırma, varsayılan, iç içe ayırma ve kalan alan toplama sunar.
- Yaymada sonra yazılan özellik önceki aynı adlıyı ezer; kopya sığdır ve nesneler adlara
  referansla bağlanır.

#### Sonraki Adım

Çıktılar bu derse kadar `console.log` argümanlarıyla veya birleştirmeyle üretildi.
Sonraki ders dizgileri ele alacak: şablon dizgileriyle biçimlendirme, arama ve bölme
yöntemleri, kod birimi ile grafem ayrımının pratik sonuçları ve sayı biçimlendirme.
Ölçüm raporunun okunabilir metni orada yazılacak.

### Dizgiler

Source: https://academia.sh/tr/kurslar/javascript-temelleri/dizgiler

Şablon dizgileri, dilimleme ve arama yöntemleri, yerel ayara bağlı harf dönüşümü, kod birimi ile grafem ayrımı ve sayı biçimlendirme.

Çıktılar bu derse kadar `console.log` argümanlarıyla üretildi. Rapor metni yazmak,
dizgilerin kendi araçlarını gerektirir. Bu ders o araçları tanımlar ve İlkel Tipler
dersinde bırakılan bir konuyu — dizginin kod birimi dizisi olmasının pratik sonucunu —
tamamlar.

Dizgiler değiştirilemezdir; buradaki her yöntem yeni bir dizgi üretir, özgün değeri
değiştirmez.

#### Şablon Dizgileri

Ters tırnakla yazılan dizgiler üç ek yetenek taşır: ifade yerleştirme, çok satırlılık ve
kaçış kurallarının farklılığı.

```js
const kayit = { istasyon: "A1", sicaklik: 21.5 };
const esik = 20;

const sablon = `${kayit.istasyon}: ${kayit.sicaklik} C (${kayit.sicaklik > esik ? "esik ustu" : "esik alti"})`;
console.log(sablon);

const cokSatir = `istasyon: ${kayit.istasyon}
sicaklik: ${kayit.sicaklik}`;
console.log(cokSatir);
console.log(`tirnak: "A1", ters tirnak: \`A1\`, dolar: \${kayit}`);
```

```
A1: 21.5 C (esik ustu)
istasyon: A1
sicaklik: 21.5
tirnak: "A1", ters tirnak: `A1`, dolar: ${kayit}
```

Yerleştirilen şey bir **ifadedir**; koşullar Koşullar ve Seçim dersindeki üçlü işleçle
yazılır. Her ifade metne çevrilir — Tip Dönüşümü ve Zorlama dersindeki metne çevirme
yordamı uygulanır, yani bir nesne yerleştirildiğinde `[object Object]` çıkar.

Satır sonu doğrudan yazılır; kaçış dizisi gerekmez. Ters tırnak ve dolar–süslü ayraç
ikilisi kaçırılmak istendiğinde ters bölü kullanılır.

Kursta metin birleştirme şablon dizgileriyle yazılır. Gerekçe, `+` işlecinin Tip
Dönüşümü ve Zorlama dersinde görülen iki yüzlü davranışından kaçınmaktır: şablonda
sonucun metin olduğu sözdiziminden bellidir.

#### Dilimleme ve Bölme

```js
const satir = "A1;21.5;48";
console.log(satir.length, satir[0], satir.at(-1));
console.log(satir.slice(0, 2), satir.slice(-2));
console.log(satir.slice(4, 2), satir.substring(4, 2));
console.log(satir.split(";"));
console.log(satir.split(";", 2));
console.log(["A1", "21.5"].join(";"));
```

```
10 A 8
A1 48
 ;2
[ 'A1', '21.5', '48' ]
[ 'A1', '21.5' ]
A1;21.5
```

Üçüncü satır iki dilimleme yönteminin ayrımını gösteriyor. `slice` negatif dizin kabul
eder ve başlangıç bitişten büyükse **boş dizgi** verir. `substring` negatif dizinleri
sıfır sayar ve argümanları gerekirse **yer değiştirir**; bu yüzden `;2` üretti. İki
sonucun aynı satırda boşlukla ayrılmış yazılması, ilkinin boş olduğunu gösteriyor. Kursta
`slice` kullanılacaktır: davranışı dizilerdeki `slice` ile aynıdır ve sürpriz üretmez.

`split` bir ayırıcıya göre böler ve ikinci argümanla sonuç sayısını sınırlar. Sınırlama
kalan metni son parçaya eklemez, atar — dosya ayrıştırırken beklenmedik veri kaybının
kaynağıdır.

#### Arama ve Değiştirme

```js
const satir = "istasyon=A1;alan=sicaklik;alan=nem";
console.log(satir.includes("alan"), satir.indexOf("alan"), satir.lastIndexOf("alan"));
console.log(satir.startsWith("istasyon"), satir.endsWith("nem"));
console.log(satir.replace("alan", "ALAN"));
console.log(satir.replaceAll("alan", "ALAN"));
console.log("  A1  ".trim() + "|", "  A1  ".trimStart() + "|");
console.log("A1".padStart(5, "0"), "A1".padEnd(5, "."), "-".repeat(10));
```

```
true 12 26
true true
istasyon=A1;ALAN=sicaklik;alan=nem
istasyon=A1;ALAN=sicaklik;ALAN=nem
A1| A1  |
000A1 A1... ----------
```

`replace` metin argümanıyla çağrıldığında **yalnızca ilk eşleşmeyi** değiştirir;
tümü için `replaceAll` kullanılır. Bu, sessiz hata üreten bir ayrımdır.

Doldurma yöntemleri hizalı çıktı üretmenin temelidir ve dolgu karakteri verilebilir.
Kırpma yöntemleri baştaki ve sondaki boşlukları atar; hangi karakterlerin boşluk sayıldığı
standartta tanımlıdır.

#### Yerel Ayara Bağlı Harf Dönüşümü

Büyük–küçük harf dönüşümü dile bağlıdır ve öntanımlı yöntemler dilden bağımsız kuralı
uygular:

```js
const ad = "istasyon";
console.log(ad.toUpperCase());
console.log(ad.toLocaleUpperCase("tr"));
console.log("ISTANBUL".toLowerCase());
console.log("ISTANBUL".toLocaleLowerCase("tr"));
```

```
ISTASYON
İSTASYON
istanbul
ıstanbul
```

Fark, noktalı ve noktasız i harflerinin ayrı harfler olduğu dillerde ortaya çıkar.
Öntanımlı dönüşüm `i` harfini noktasız büyük harfe çevirir; yerel ayarlı dönüşüm noktalı
harfi üretir.

Buradan iki kural çıkar. Kullanıcıya gösterilecek metin dönüştürülüyorsa yerel ayar
belirtilmelidir. Buna karşılık **karşılaştırma amacıyla** yapılan dönüşümde yerel ayarlı
biçim kullanılmamalıdır: aynı metin farklı yerel ayarlarda farklı sonuç verir ve
karşılaştırma tutarsızlaşır. Anahtar karşılaştırmaları dilden bağımsız biçimle yapılır.

#### Kod Birimi ve Grafem

İlkel Tipler dersinde dizginin kod birimi dizisi olduğu söylenmişti. Sonucu dilimlemede
görünür:

```js
const metin = "A1😀";
console.log(metin.length);
console.log([...metin].length);
console.log([...metin]);
console.log(Array.from(metin).length);
```

```
4
3
[ 'A', '1', '😀' ]
3
```

`length` dört verdi çünkü son karakter iki kod biriminden oluşur — Bilgisayarlar Nasıl
Çalışır kursundaki vekil çift. Yayma ve `Array.from`, dizgiyi **kod noktalarına** göre
dolaşır ve üç eleman verir.

Kod birimi sınırında kesmek metni bozar:

```js
const metin = "A1😀";
console.log(metin.slice(0, 3).length);
console.log([...metin].slice(0, 3).join(""));
```

```
3
A1😀
```

İlk dilimleme, çift kod biriminin yalnızca ilkini aldı; ortaya tek başına anlamı olmayan
bir kod birimi çıktı ve gösterildiğinde bozuk görünür. İkinci yol kod noktası dizisi
üzerinde çalıştığı için karakteri bölmedi.

Grafem — kullanıcının tek karakter saydığı birim — kod noktasından da büyük olabilir;
birleştirici işaretler bunun örneğidir:

```js
const birlesik = "é";
const tek = "é";
console.log(birlesik.length, tek.length, birlesik === tek);
console.log(birlesik.normalize("NFC") === tek);
```

```
2 1 false
true
```

İki dizgi aynı görünür ama farklı kod noktası dizileridir; karşılaştırma yanlış verir.
Normalleştirme ikisini ortak biçime getirir. Metin karşılaştırması yapan kod, girdileri
önce normalleştirmelidir.

#### Sayı Biçimlendirme

```js
console.log((21.456).toFixed(2));
console.log((0.1 + 0.2).toFixed(2));
console.log((1.005).toFixed(2));
console.log((21.5).toString(2));
console.log(String(1e21), String(1e-7));
```

```
21.46
0.30
1.00
10101.1
1e+21 1e-7
```

`toFixed` belirtilen basamak sayısına yuvarlar ve **metin** döndürür. Üçüncü satır
sürpriz gibi görünür ama İlkel Tipler dersindeki modelin sonucudur: `1.005` değeri kayan
noktalı gösterimde tam olarak temsil edilemez ve saklanan değer `1.005`'ten biraz
küçüktür, bu yüzden aşağı yuvarlanır. Yuvarlama hatası biçimlendirmede değil, sayının
kendisindedir.

`toString` taban argümanı alır; Bilgisayarlar Nasıl Çalışır kursundaki İkilik Sayı
Sistemi dersindeki gösterim böyle üretilir. Son satır, çok büyük ve çok küçük sayıların
üstel gösterime geçtiğini gösteriyor; sınırlar standartta tanımlıdır.

#### Ölçüm Raporunu Yazmak

Bu dersin araçları birleştiğinde hizalı bir rapor çıkar:

```js
const kayitlar = [
  { istasyon: "A1", sicaklik: 21.5, nem: 48 },
  { istasyon: "A2", sicaklik: 19.75, nem: 55 },
  { istasyon: "B1", sicaklik: 23, nem: null },
];

const baslik = `${"istasyon".padEnd(10)}${"sicaklik".padStart(9)}${"nem".padStart(6)}`;
const cizgi = "-".repeat(baslik.length);

const satirlar = kayitlar.map((k) => {
  const nemMetni = k.nem == null ? "-" : String(k.nem);
  return `${k.istasyon.padEnd(10)}${k.sicaklik.toFixed(2).padStart(9)}${nemMetni.padStart(6)}`;
});

console.log([baslik, cizgi, ...satirlar].join("\n"));
```

```
istasyon   sicaklik   nem
-------------------------
A1            21.50    48
A2            19.75    55
B1            23.00     -
```

Eksik nem alanı `-` ile gösterildi; `== null` denetimi Eşitlik Karşılaştırmaları
dersindeki istisnadır. `toFixed(2)` tüm sıcaklıkları aynı basamak sayısına getirdi —
`23` değeri `23.00` olarak yazıldı. Satırlar `join` ile tek bir metne bağlandı ve tek
çağrıda yazıldı.

Hizalama kod birimi sayısına dayanır. Rapor Latin harfleri dışında karakterler
içeriyorsa, doldurma yöntemleri görsel genişliği değil kod birimi sayısını
dengeleyeceği için sütunlar kayabilir.

#### Özet

- Şablon dizgileri ifade yerleştirir, çok satırlıdır ve kursta metin birleştirmenin
  tek biçimidir.
- `slice` negatif dizin kabul eder ve ters aralıkta boş dizgi verir; `substring`
  argümanları yer değiştirir.
- `replace` yalnızca ilk eşleşmeyi değiştirir; tümü için `replaceAll` gerekir.
- Harf dönüşümü yerel ayara bağlıdır; gösterim için yerel ayarlı, karşılaştırma için
  dilden bağımsız biçim kullanılır.
- `length` kod birimi sayar; kod noktası bazlı işlem için yayma veya `Array.from`
  kullanılır ve karşılaştırmadan önce normalleştirme gerekir.
- `toFixed` metin döndürür ve yuvarlama, sayının kayan noktalı temsilinden etkilenir.

#### Sonraki Adım

Rapor okunabilir bir metin üretti, ama makinenin geri okuyabileceği bir biçim değil.
Sonraki ders JSON'u ele alacak: dizileştirme ve ayrıştırma, biçimin desteklemediği
değerler, döngüsel başvurunun ürettiği hata ve dönüşümü uyarlayan geri çağrılar. Büyük
Tam Sayılar dersinde bırakılan dizileştirme sorunu da orada tamamlanacak.

### JSON

Source: https://academia.sh/tr/kurslar/javascript-temelleri/json

Dizileştirme ve ayrıştırma, biçimin desteklemediği değerler, döngüsel başvuru, dönüşümü uyarlayan geri çağrılar ve sayı duyarlığının sınırı.

Önceki ders okunabilir bir rapor metni üretti. O metin insan içindi; makinenin geri
okuyabileceği bir biçim değil. Bu ders JSON'u ele alıyor: verinin metne çevrilip geri
okunması.

JSON, JavaScript sözdiziminden türemiş ama ondan bağımsız bir veri biçimidir. Bu köken,
iki dilin birbirine yakın görünmesine ama tam örtüşmemesine yol açar. Ayrımı bilmek,
sessiz veri kaybını önler.

#### Dizileştirme ve Ayrıştırma

```js
const kayit = { istasyon: "A1", sicaklik: 21.5, nem: null, dogrulandi: false };
const metin = JSON.stringify(kayit);
console.log(metin);
console.log(typeof metin);
console.log(JSON.stringify(kayit, null, 2));
const geri = JSON.parse(metin);
console.log(geri, geri.sicaklik + 1);
console.log(kayit === geri);
```

```
{"istasyon":"A1","sicaklik":21.5,"nem":null,"dogrulandi":false}
string
{
  "istasyon": "A1",
  "sicaklik": 21.5,
  "nem": null,
  "dogrulandi": false
}
{ istasyon: 'A1', sicaklik: 21.5, nem: null, dogrulandi: false } 22.5
false
```

`JSON.stringify` bir değeri metne çevirir; üçüncü argüman girinti miktarıdır ve
okunabilir çıktı üretir. `JSON.parse` metinden yeni bir değer üretir.

Son satır önemlidir: çevrim sonucu **yeni bir nesnedir**. Eşitlik Karşılaştırmaları
dersindeki kimlik kuralı gereği özgün nesneye eşit değildir. Bu, aynı zamanda derin kopya
almanın bir yoludur — ancak yalnızca biçimin desteklediği değerler için.

Çıktıdaki üçüncü blok JSON metnidir: anahtarlar çift tırnaklıdır. Sondaki nesne
gösterimi ise çalışma zamanının inceleme biçimidir; tırnaksız anahtarlar ve tek tırnaklı
metinler oradan gelir. İkisi farklı şeylerdir.

#### Biçimin Desteklemediği Değerler

JSON'un değer kümesi dilin değer kümesinden dardır. Desteklenmeyen değerler sessizce
işlenir:

```js
const kayit = {
  istasyon: "A1",
  eksik: undefined,
  fn: () => 1,
  sembol: Symbol("x"),
  gecersiz: NaN,
  sonsuz: Infinity,
};
console.log(JSON.stringify(kayit));
console.log(JSON.stringify([undefined, () => 1, NaN]));
```

```
{"istasyon":"A1","gecersiz":null,"sonsuz":null}
[null,null,null]
```

İki farklı davranış var. Nesne özelliklerinde `undefined`, fonksiyon ve sembol değerleri
**tümüyle atlanır**: anahtar çıktıda hiç görünmez. Dizi elemanlarında aynı değerler
`null`'a çevrilir, çünkü dizinin uzunluğu korunmalıdır.

`NaN` ve `Infinity` her iki durumda da `null` olur. Bu, biçimin bu değerleri hiç
tanımamasındandır. Sonuç sessizdir: geçersiz bir ölçüm, ayrıştırıldığında "bilinçli
olarak boş" anlamına gelen `null` değerine dönüşür. İlkel Tipler dersinde kurulan
`null`–`undefined` ayrımı çevrimden sağ çıkmaz.

İki durumda ise hata verilir:

```js
const a = { istasyon: "A1" };
a.kendisi = a;
try {
  JSON.stringify(a);
} catch (hata) {
  console.log(hata.name);
}
try {
  JSON.stringify({ sayac: 1n });
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
TypeError
TypeError: Do not know how to serialize a BigInt
```

Döngüsel başvuru — bir nesnenin doğrudan veya dolaylı olarak kendisini içermesi —
sonsuz bir metin üretirdi. Büyük tam sayılar ise Büyük Tam Sayılar dersinde
tartışıldığı gibi biçimin duyarlık garantisi olmadığı için reddedilir. İlk hatanın
iletisi çalışma zamanına göre değiştiği için yalnızca adı yazdırıldı.

#### Dönüşümü Uyarlamak

Her iki yöntem de bir geri çağrı alabilir. Dizileştirmede geri çağrı her anahtar–değer
çifti için çağrılır ve döndürdüğü değer yazılır; `undefined` döndürmek alanı atlar.

```js
const kayit = {
  istasyon: "A1",
  zamanNs: 1700000000123456789n,
  sicaklik: 21.5,
  gizli: "yazilmasin",
};

const metin = JSON.stringify(kayit, (anahtar, deger) => {
  if (anahtar === "gizli") return undefined;
  if (typeof deger === "bigint") return deger.toString();
  return deger;
});
console.log(metin);

const geri = JSON.parse(metin, (anahtar, deger) =>
  anahtar === "zamanNs" ? BigInt(deger) : deger,
);
console.log(geri.zamanNs === kayit.zamanNs, typeof geri.zamanNs);
```

```
{"istasyon":"A1","zamanNs":"1700000000123456789","sicaklik":21.5}
true bigint
```

Büyük Tam Sayılar dersinde bırakılan sorun burada tamamlandı: değer metin olarak
yazıldı, okunurken geri çevrildi ve son basamağına kadar korundu. `gizli` alanı çıktıya
girmedi.

İki geri çağrı simetriktir ve birlikte tasarlanmalıdır: yazan taraf hangi alanı hangi
biçime çevirdiyse, okuyan taraf aynı alanı geri çevirmelidir. Aksi halde tip sessizce
değişir.

Dizileştirmede geri çağrı yerine bir alan listesi de verilebilir:

```js
const kayit = { istasyon: "A1", sicaklik: 21.5, nem: 48 };
console.log(JSON.stringify(kayit, ["istasyon", "sicaklik"]));
```

```
{"istasyon":"A1","sicaklik":21.5}
```

#### Sayı Duyarlığının Sınırı

JSON metnindeki sayılar, ayrıştırılırken dilin sayı tipine çevrilir. İlkel Tipler
dersindeki güvenli aralık burada da geçerlidir:

```js
console.log(JSON.parse("9007199254740993"));
console.log(JSON.parse("0.1") + JSON.parse("0.2"));
console.log(JSON.stringify(0.1 + 0.2));
```

```
9007199254740992
0.30000000000000004
0.30000000000000004
```

İlk satır sessiz bir kayıptır: metinde doğru yazılmış bir tam sayı, okunduğunda başka bir
değere düştü. Biçim bu değeri taşıyabiliyordu; kaybeden ayrıştırıcı değil, hedef tiptir.
Kayıpsız aktarım için değer metin olarak yazılır — yukarıdaki geri çağrı kalıbı budur.

Son iki satır kayan noktalı gösterimin çevrimden sağ çıktığını gösteriyor: yazılan metin
aynı bit örüntüsüne geri döner.

#### Ayrıştırma Katıdır

JSON dilbilgisi, JavaScript nesne sözdiziminden dardır:

```js
const bozuklar = ['{"a":1,}', "{'a':1}", "{a:1}", ""];
for (const m of bozuklar) {
  try {
    JSON.parse(m);
    console.log(JSON.stringify(m), "-> gecerli");
  } catch (hata) {
    console.log(JSON.stringify(m), "->", hata.name);
  }
}
```

```
"{\"a\":1,}" -> SyntaxError
"{'a':1}" -> SyntaxError
"{a:1}" -> SyntaxError
"" -> SyntaxError
```

Dördü de JavaScript kaynağında geçerli ya da zararsız yazımlardır; JSON'da değildir.
Sondaki virgül, tek tırnak, tırnaksız anahtar ve boş metin reddedilir. Bu katılık,
biçimin farklı dillerde aynı şekilde okunmasını sağlar.

Hata adı `SyntaxError`'dır; ileti metni ayrıştırıcıya göre değişir. Hata Yönetimi
dersindeki kural burada uygulanır: dallanma ada göre yapılır.

#### Ölçüm Betiğine Uygulama

Kursun ekseni artık bir tam çevrim yazabilir: metinden oku, dönüştür, metne yaz.

```js
const gelen = `[
  {"istasyon":"A1","sicaklik":21.5,"nem":48},
  {"istasyon":"A2","sicaklik":19.75,"nem":null},
  {"istasyon":"B1","sicaklik":23,"nem":41}
]`;

const kayitlar = JSON.parse(gelen);
const zenginlestirilmis = kayitlar.map((k) => ({
  ...k,
  sapma: Number((k.sicaklik - 20).toFixed(2)),
  nemVar: k.nem != null,
}));

console.log(JSON.stringify(zenginlestirilmis, null, 2));
```

```
[
  {
    "istasyon": "A1",
    "sicaklik": 21.5,
    "nem": 48,
    "sapma": 1.5,
    "nemVar": true
  },
  {
    "istasyon": "A2",
    "sicaklik": 19.75,
    "nem": null,
    "sapma": -0.25,
    "nemVar": false
  },
  {
    "istasyon": "B1",
    "sicaklik": 23,
    "nem": 41,
    "sapma": 3,
    "nemVar": true
  }
]
```

Üç karar birlikte alındı. Dönüşüm yeni nesneler üretti; Nesneler dersindeki kural gereği
girdi değiştirilmedi. Sapma değeri `toFixed` ile yuvarlanıp `Number` ile sayıya geri
çevrildi — aksi halde JSON'a metin olarak yazılırdı. `nem` alanının varlığı `!= null`
denetimiyle sınandı, böylece `null` değeri de eksiklik sayıldı.

Ayrıştırılan metnin dışarıdan gelmesi durumunda ayrıştırma bir `try`/`catch` içine
alınmalıdır; bozuk girdi programı durdurmamalıdır.

#### Özet

- `JSON.stringify` değeri metne, `JSON.parse` metni yeni bir değere çevirir; çevrim
  sonucu özgün nesneye eşit değildir.
- Nesne özelliklerinde `undefined`, fonksiyon ve sembol atlanır; dizi elemanlarında
  `null`'a çevrilir. `NaN` ve `Infinity` her durumda `null` olur.
- Döngüsel başvuru ve büyük tam sayılar dizileştirmede hata verir.
- Geri çağrılar desteklenmeyen tipleri kayıpsız aktarmayı sağlar; yazan ve okuyan taraf
  simetrik yazılır.
- JSON sayıları dilin sayı tipine çevrilir; güvenli aralığın üstündeki tam sayılar
  sessizce yuvarlanır.
- JSON dilbilgisi katıdır: sondaki virgül, tek tırnak ve tırnaksız anahtar reddedilir.

#### Sonraki Adım

Ölçüm kayıtları hâlâ zamansızdır: hangi okumanın ne zaman alındığı bilinmiyor. Sonraki
ders tarih ve zaman gösterimini ele alacak — zaman damgası kavramı, saat dilimi ve yerel
saat ayrımı, ayrıştırma tuzakları ve biçimlendirme. Kursun kapanışı da orada yapılacak.

### Tarih ve Zaman

Source: https://academia.sh/tr/kurslar/javascript-temelleri/tarih-ve-zaman

Zaman damgası modeli, yerel saat ile eşgüdümlü evrensel zaman ayrımı, ayrıştırma tuzakları, tarih aritmetiği ve yerel ayarlı biçimlendirme.

Ölçüm kayıtları bu derse kadar zamansızdı: hangi okumanın ne zaman alındığı bilinmiyordu.
Bu ders zaman gösterimini ekliyor ve kursu kapatıyor.

Tarih–zaman, dilin en çok yanlış kullanılan alanıdır. Nedeni tek bir karışıklıktır: bir
**an** ile o anın bir **gösterimi** aynı şey değildir. Bu ders ayrımı kurar ve tuzakları
o ayrımdan türetir.

Aşağıdaki örneklerin çıktısı çalıştırıldığı makinenin saat dilimine bağlıdır. Yeniden
üretilebilir olması için hepsi evrensel zaman diliminde (`TZ=UTC`) çalıştırılmıştır. Makinenizin
saat dilimi farklıysa yerel saate bağlı satırlar değişir; hangi satırların değiştiği ilgili
bölümlerde ayrıca söylenir.

#### Zaman Damgası

Bir tarih nesnesinin taşıdığı tek veri, sabit bir başlangıç anından itibaren geçen
milisaniye sayısıdır. Bu sayı **zaman damgasıdır** ve saat diliminden bağımsızdır.

```js
const damga = 1709287200000;
const t = new Date(damga);
console.log(t.getTime());
console.log(t.toISOString());
console.log(Number(t) === damga);
```

```
1709287200000
2024-03-01T10:00:00.000Z
true
```

Aynı betik başka bir saat diliminde çalıştırıldığında bu üç satır **değişmez**. Damga bir
andır; ISO gösterimi de eşgüdümlü evrensel zamana göre yazıldığı için sabittir.

Nesne sayı bağlamında damgasına çevrilir; bu, tarih aritmetiğinin temelidir.

#### Yerel Saat ve Evrensel Zaman

Okuma yöntemleri iki ailededir. Adında `UTC` geçenler evrensel zamana, geçmeyenler
makinenin yerel saat dilimine göre değer verir:

```js
const t = new Date(1709287200000);
console.log("ISO      :", t.toISOString());
console.log("yerel yil:", t.getFullYear(), "ay:", t.getMonth(), "gun:", t.getDate());
console.log("yerel saat:", t.getHours());
console.log("UTC saat  :", t.getUTCHours());
console.log("fark(dk)  :", t.getTimezoneOffset());
```

`TZ=UTC` ile:

```
ISO      : 2024-03-01T10:00:00.000Z
yerel yil: 2024 ay: 2 gun: 1
yerel saat: 10
UTC saat  : 10
fark(dk)  : 0
```

Evrensel zamandan üç saat ileride olan `TZ=Europe/Istanbul` ile aynı betik, yerel saat
satırında `13` ve fark satırında `-180` verir; diğer satırlar değişmez.

İki tuzak burada birleşir. Birincisi, **ay numarası sıfırdan başlar**: mart ayı `2`
değerini verdi. Gün ve yıl ise birden başlar. Bu tutarsızlık dilin ilk halinden gelir ve
geriye dönük uyumluluk nedeniyle korunmuştur.

İkincisi, saat dilimi farkının **işareti terstir**: evrensel zamandan üç saat ileride
olan bir dilim `-180` verir. Değer, yerel saatten evrensel zamana gitmek için eklenmesi
gereken dakika sayısıdır.

#### Ayrıştırma Tuzağı

Metinden tarih üretmek, biçime göre farklı saat dilimi varsayar:

```js
const bicimler = [
  "2024-03-01T10:00:00Z",
  "2024-03-01T10:00:00",
  "2024-03-01",
  "2024-03-01T10:00:00+03:00",
];
for (const m of bicimler) {
  console.log(m.padEnd(26), new Date(m).toISOString());
}
```

`TZ=UTC` ile:

```
2024-03-01T10:00:00Z       2024-03-01T10:00:00.000Z
2024-03-01T10:00:00        2024-03-01T10:00:00.000Z
2024-03-01                 2024-03-01T00:00:00.000Z
2024-03-01T10:00:00+03:00  2024-03-01T07:00:00.000Z
```

Dört satır dört farklı kural gösteriyor.

Birinci ve dördüncü satırlarda saat dilimi metnin içinde yazılıdır; sonuç makineden
bağımsızdır. İkinci satırda saat dilimi yazılmamış ve **yerel saat** varsayılmıştır; aynı
metin başka bir makinede başka bir ana karşılık gelir. Burada makinenin yerel saat dilimi
evrensel zaman olduğu için ikinci satır birincisiyle aynı görünür, ama bu bir rastlantıdır:
`TZ=Europe/Istanbul` ile aynı metin `2024-03-01T07:00:00.000Z` verir. Üçüncü satırda yalnızca tarih
verilmiş ve **evrensel zaman** varsayılmıştır — ikinci satırın tam tersi.

Kural buradan çıkar: **ayrıştırılacak her zaman metni saat dilimi bilgisi taşımalıdır.**
Taşımıyorsa, hangi kuralın uygulanacağı biçime bağlıdır ve iki biçim birbirine zıt
davranır.

Ayrıştırılamayan metin hata vermez; geçersiz bir tarih üretir:

```js
const t = new Date("bozuk");
console.log(String(t));
console.log(Number.isNaN(t.getTime()));
try {
  t.toISOString();
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
```

```
Invalid Date
true
RangeError: Invalid time value
```

Geçersizlik, damganın `NaN` olmasıyla saptanır. Denetim yapılmadan kullanıldığında hata,
ancak biçimlendirme aşamasında ortaya çıkar — kaynağından uzakta.

#### Tarih Aritmetiği

Aritmetik zaman damgası üzerinden yapılır; nesnelerin çıkarılması damga farkını verir:

```js
const bas = new Date("2024-03-01T10:00:00Z");
const son = new Date("2024-03-01T10:00:00.800Z");
console.log(son.getTime() - bas.getTime(), "ms");

const birGunSonra = new Date(bas.getTime() + 24 * 60 * 60 * 1000);
console.log(birGunSonra.toISOString());
```

```
800 ms
2024-03-02T10:00:00.000Z
```

Sabit milisaniye eklemek **süre** ekler, **takvim günü** eklemez. İkisi çoğu zaman
aynıdır; yaz saati geçişlerinin olduğu dilimlerde bir günlük süre bir takvim gününe eşit
olmayabilir. Takvim işlemleri için ayarlayıcı yöntemler kullanılır:

```js
const bas = new Date("2024-03-01T10:00:00Z");
const kopya = new Date(bas.getTime());
kopya.setUTCDate(kopya.getUTCDate() + 1);
console.log(kopya.toISOString(), bas.toISOString());
```

```
2024-03-02T10:00:00.000Z 2024-03-01T10:00:00.000Z
```

Tarih nesneleri **değiştirilebilirdir**: ayarlayıcı yöntemler nesneyi yerinde günceller.
Bu yüzden önce kopya alındı; alınmasaydı `bas` da değişirdi. Nesneler dersindeki kural
burada da geçerlidir — dönüşüm girdisini değiştirmez.

#### Biçimlendirme

Kullanıcıya gösterilecek metin, yerel ayar ve saat dilimi açıkça verilerek üretilir:

```js
const t = new Date("2024-03-01T10:00:00Z");
const bicim = new Intl.DateTimeFormat("tr-TR", {
  dateStyle: "short",
  timeStyle: "medium",
  timeZone: "Europe/Istanbul",
});
console.log(bicim.format(t));

const utcBicim = new Intl.DateTimeFormat("tr-TR", {
  dateStyle: "short",
  timeStyle: "medium",
  timeZone: "UTC",
});
console.log(utcBicim.format(t));
console.log(t.toISOString().slice(0, 19).replace("T", " "));
```

```
1.03.2024 13:00:00
1.03.2024 10:00:00
2024-03-01 10:00:00
```

Aynı an iki farklı saatle gösterildi; ikisi de doğrudur. Fark, hangi saat diliminde
bakıldığıdır.

Biçimlendiricinin ürettiği metnin ayrıntıları — ayırıcılar, basamak sayısı, harf
kullanımı — yerel ayar verilerine bağlıdır ve çalışma zamanları arasında değişebilir.
Bu yüzden biçimlendirilmiş metin **gösterim içindir**; ayrıştırılmak veya
karşılaştırılmak üzere saklanmaz. Saklanacak biçim ISO gösterimidir.

#### JSON ile Birlikte

Tarih nesneleri dizileştirmede ISO metnine çevrilir, ama geri okunduğunda nesne olmaz:

```js
const kayit = { istasyon: "A1", zaman: new Date("2024-03-01T10:00:00Z") };
const metin = JSON.stringify(kayit);
console.log(metin);
const geri = JSON.parse(metin);
console.log(typeof geri.zaman, geri.zaman);

const geriCanli = JSON.parse(metin, (anahtar, deger) =>
  anahtar === "zaman" ? new Date(deger) : deger,
);
console.log(geriCanli.zaman instanceof Date);
```

```
{"istasyon":"A1","zaman":"2024-03-01T10:00:00.000Z"}
string 2024-03-01T10:00:00.000Z
true
```

Çevrim tek yönlü asimetriktir: yazma kendiliğinden olur; okuma geri çağrı ister. JSON
dersindeki simetri kuralı burada da uygulanır.

#### Ölçüm Betiğinin Son Hali

Kursun ekseni tamamlanıyor: metinden okunan, zamanla sıralanan ve biçimlendirilmiş bir
ölçüm listesi.

```js
const gelen = [
  { istasyon: "A1", zaman: "2024-03-01T10:00:00Z", sicaklik: 21.5 },
  { istasyon: "A1", zaman: "2024-03-01T10:15:00Z", sicaklik: 22.75 },
  { istasyon: "B1", zaman: "2024-03-01T10:05:00Z", sicaklik: 23 },
];

const bicim = new Intl.DateTimeFormat("tr-TR", {
  timeStyle: "short",
  timeZone: "Europe/Istanbul",
});

const kayitlar = gelen
  .map((k) => ({ ...k, zaman: new Date(k.zaman) }))
  .filter((k) => !Number.isNaN(k.zaman.getTime()))
  .sort((a, b) => a.zaman - b.zaman);

for (const k of kayitlar) {
  console.log(
    `${k.istasyon.padEnd(4)} ${bicim.format(k.zaman).padStart(6)} ${k.sicaklik.toFixed(2).padStart(7)}`,
  );
}

const kapsam = kayitlar[kayitlar.length - 1].zaman - kayitlar[0].zaman;
console.log("kapsanan sure (dk):", kapsam / 60000);
```

```
A1    13:00   21.50
B1    13:05   23.00
A1    13:15   22.75
kapsanan sure (dk): 15
```

Zincirin her adımı kursun bir dersinden geliyor. Yayma ile yeni nesneler üretildi
(Nesneler), geçersiz tarihler süzüldü (Doğruluk Değerleri ve bu ders), sıralama
karşılaştırma fonksiyonuyla yazıldı (Diziler), metin şablon dizgisiyle hizalandı
(Dizgiler) ve süre farkı damga aritmetiğiyle hesaplandı.

Sıralama karşılaştırması `a.zaman - b.zaman` yazımıyla yapıldı; çıkarma işleci tarih
nesnelerini damgalarına çevirdiği için sonuç sayıdır. Bu, Tip Dönüşümü ve Zorlama
dersindeki kuralın yararlı kullanıldığı ender yerlerden biridir.

#### Özet

- Tarih nesnesi tek bir veri taşır: saat diliminden bağımsız zaman damgası. Gösterim ise
  saat dilimine bağlıdır.
- Ay numarası sıfırdan, gün ve yıl birden başlar; saat dilimi farkının işareti terstir.
- Saat dilimi yazılmamış zaman metni yerel saat, yalnızca tarih içeren metin evrensel
  zaman varsayılır; ayrıştırılacak metin saat dilimi taşımalıdır.
- Geçersiz tarih hata vermez; damgası `NaN` olur ve ancak biçimlendirmede hata üretir.
- Aritmetik damga üzerinden yapılır; ayarlayıcı yöntemler nesneyi yerinde değiştirdiği
  için önce kopya alınır.
- Biçimlendirilmiş metin gösterim içindir; saklama ve karşılaştırma ISO gösterimiyle
  yapılır.

#### Kurs Kapanışı

Bu kurs JavaScript'in değer modelini ve temel yapılarını kurdu. Dört konu birbirini
tamamladı: dilin nasıl yürütüldüğü ve adların nasıl bağlandığı; hangi değerlerin
bulunduğu ve nasıl dönüştürülüp karşılaştırıldıkları; akışın ve fonksiyonların nasıl
yazıldığı; yerleşik veri yapılarının nasıl kullanıldığı.

Kurs boyunca tek bir örnek üzerinde çalışıldı. Ölçüm listesi, dört sayıdan oluşan bir
diziyken başladı; metinden okunan, doğrulanan, gruplanan, biçimlendirilen ve JSON olarak
geri yazılan bir veri kümesine dönüştü. Her ders o listeye bir yetenek ekledi ve
eklediği yeteneğin sınırını da gösterdi.

Bilinçli olarak dışarıda bırakılan üç alan var. Birincisi, nesnelerin arkasındaki
prototip modeli: özelliklerin nasıl arandığı, sınıf sözdiziminin bu modele karşılığı ve
`this` bağlamasının kuralları. İkincisi, kapanışların durum taşımak için kullanılması.
Üçüncüsü, eşzamansız yürütme.

İlk ikisi **JavaScript'te Nesneler ve Fonksiyonlar** kursunun konusudur. O kurs, bu
kursta birkaç kez ertelenen soruları yanıtlar: bir dizinin `map` yöntemi nereden gelir,
ok fonksiyonunun `prototype` özelliğinin bulunmaması neyi engeller, nesne yöntemlerinde
`this` neye bağlanır ve bir fonksiyonun döndürdüğü fonksiyon dış değişkeni nasıl
hatırlar. Bu kursun kurduğu değer modeli, oradaki tartışmanın zeminidir.

Üçüncüsü daha sonra gelir. Bu kursta veri hep elimizin altındaydı; bir ağ isteğinden ya
da dosya okumasından gelseydi sonucun hazır olmasını beklemek gerekirdi ve program bu
sırada durmazdı. Olay döngüsü, sözler ve eşzamansız bekleme müfredatın ilerleyen
kursunun konusudur. Sıradaki kurs olan JavaScript'te Nesneler ve Fonksiyonlar ise önce
değerlerin arkasındaki yapıyı açar.

