---
title: "JavaScript'te Nesneler ve Fonksiyonlar"
source: 'https://academia.sh/tr/kurslar/javascript-nesne-modeli'
language: tr
updated: '2026-08-17T04:41:35+00:00'
license: 'CC BY-SA 4.0'
lessons: 17
outcomes:
    - 'Prototip zincirini ve sınıf sözdiziminin bu zincire karşılığını açıklar'
    - 'this bağlamının dört bağlanma kuralını ayırt eder'
    - 'Kapanış (closure) kullanarak durum taşıyan fonksiyonlar yazar'
---

# JavaScript'te Nesneler ve Fonksiyonlar

Prototip zinciri ve özellik arama sırası, sınıf sözdiziminin bu zincire karşılığı, this bağlanma kuralları, kapanışlar ve ileri koleksiyonlar.

## Prototip Modeli

Prototip bağı ve özellik arama zinciri, nesneden nesneye kalıtım kalıpları, sınıf sözdiziminin zincirdeki karşılığı, özellik tanımlayıcıları ve değişmezlik teknikleri.

### Nesne Prototipi

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/nesne-prototipi

Her nesnenin taşıdığı prototip bağı, özelliklerin bu bağ üzerinden aranma sırası, kendi özellik ile devralınan özellik ayrımı ve zincirin sonu.

JavaScript Temelleri kursu nesneyi anahtar–değer eşlemesi olarak tanıttı: özellik yazılır,
okunur, silinir. Bu tanım günlük kullanım için yeterlidir ama bir soruyu yanıtsız bırakır.
Boş bir nesne yaratıp `toString` özelliğini okuduğunuzda tanımsız değil, bir fonksiyon
elde edersiniz. O fonksiyonu oraya kimse yazmadı.

Yanıt, dilin nesne modelinin merkezinde durur: her nesne, kendi özelliklerine ek olarak
**başka bir nesneye bir bağ** taşır. Aradığınız özellik nesnenin kendisinde yoksa, arama
bu bağ üzerinden devam eder. Bu ders o bağı kurar; kursun geri kalanı — sınıf sözdizimi de
dahil — onun üzerine oturur.

#### Aynı Davranışın İki Kopyası

Kurs boyunca tek bir somut örnek üzerinde çalışacağız: bir sensörden okunan **ölçüm
kaydı**. Kayıt üç veri taşır (sensör kimliği, ölçülen değer, ölçüm anı) ve en az bir
davranış sunar (kendini okunabilir biçimde yazdırmak). En doğrudan kuruluş, her kaydı
kendi başına bir nesne değişmezi olarak yazmaktır.

```js
const kayitBir = {
  sensor: "S-01",
  deger: 21.4,
  zaman: 1000,
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
};

const kayitIki = {
  sensor: "S-02",
  deger: 19.8,
  zaman: 1060,
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
};

console.log(kayitBir.bicimle());
console.log(kayitIki.bicimle());
console.log(kayitBir.bicimle === kayitIki.bicimle);
```

```
S-01@1000: 21.4
S-02@1060: 19.8
false
```

Son satır sorunu görünür kılar. İki kayıt aynı davranışı sunar, ama bellekte **iki ayrı
fonksiyon nesnesi** vardır; kimlik karşılaştırması bunu söyler. Bin ölçüm için bin
kopya oluşur ve biçimlendirme kuralı değiştiğinde bin yerde değişmesi gerekir.

Programlama Temelleri kursundaki Nesneye Dayalı Programlamaya Giriş dersi bu soruna sınıf
kavramıyla yanıt vermişti: davranış sınıfta bir kez yazılır, nesneler onu paylaşır.
JavaScript'in yanıtı bir adım daha alttadır ve sınıf gerektirmez.

#### Prototip Bağı

Her nesne, yaratıldığı anda başka bir nesneye — ya da `null` değerine — bağlanır. Bu
bağa **prototip (prototype)** denir. Bağ, nesnenin sıradan bir özelliği değildir; dilin
belirtiminde `[[Prototype]]` adlı iç bir alan olarak geçer ve `Object.getPrototypeOf`
işleviyle okunur.

`Object.create` bağı doğrudan kurar: verilen nesneyi prototip alan yeni bir nesne üretir.

```js
const olcumDavranisi = {
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
  esikAstiMi(esik) {
    return this.deger > esik;
  },
};

function olcumKaydi(sensor, deger, zaman) {
  const kayit = Object.create(olcumDavranisi);
  kayit.sensor = sensor;
  kayit.deger = deger;
  kayit.zaman = zaman;
  return kayit;
}

const kayitBir = olcumKaydi("S-01", 21.4, 1000);
const kayitIki = olcumKaydi("S-02", 19.8, 1060);

console.log(kayitBir.bicimle());
console.log(kayitIki.esikAstiMi(20));
console.log(kayitBir.bicimle === kayitIki.bicimle);
console.log(Object.getPrototypeOf(kayitBir) === olcumDavranisi);
```

```
S-01@1000: 21.4
false
true
true
```

Üçüncü satır artık `true` verir: iki kayıt aynı fonksiyon nesnesini kullanır, çünkü
fonksiyon kayıtlarda değil, ortak prototipte durur. Dördüncü satır bağın kendisini
sınar — nesne modeliyle ilgili her iddia bu ders boyunca bu biçimde, doğrudan
sınanabilir bir karşılaştırmayla gösterilecektir.

Veri her kayıtta ayrı, davranış tek yerde. Bu ayrım prototip modelinin tüm işlevidir:
**değişen kısım nesnede, değişmeyen kısım zincirde durur.**

#### Özellik Arama Sırası

Bir özelliği okuduğunuzda çalışma zamanı şu sırayı izler:

1. Nesnenin kendi özellikleri arasında ara. Bulunursa değeri döndür.
2. Bulunamazsa prototipine geç, aynı aramayı orada yap.
3. Zincir `null` değerine ulaşana kadar sürdür. Hiçbir yerde bulunmazsa `undefined` döndür.

Arama ilk bulduğunda durduğu için, nesnenin kendi özelliği aynı adlı prototip özelliğini
**gölgeler (shadowing)**. Programlama Temelleri kursundaki Kapsam ve Yaşam Süresi dersinde
iç kapsamın dış kapsamdaki adı gölgelemesi anlatılmıştı; buradaki mekanizma aynı fikrin
nesneler üzerindeki karşılığıdır.

```js
const olcumDavranisi = {
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
};

const kayit = Object.create(olcumDavranisi);
kayit.sensor = "S-01";
kayit.deger = 21.4;
kayit.zaman = 1000;

console.log(kayit.bicimle());

kayit.bicimle = function () {
  return `[ham] ${this.deger}`;
};

console.log(kayit.bicimle());
console.log(Object.hasOwn(kayit, "bicimle"));
console.log(olcumDavranisi.bicimle.call(kayit));

delete kayit.bicimle;
console.log(kayit.bicimle());
```

```
S-01@1000: 21.4
[ham] 21.4
true
S-01@1000: 21.4
S-01@1000: 21.4
```

Üç gözlem önemlidir. Birincisi, atama prototipteki fonksiyonu **değiştirmez**; kayda ait
yeni bir özellik yaratır. Yazma işlemi zincirde yukarı çıkmaz, her zaman nesnenin
kendisine yazar. İkincisi, gölgelenen özellik yok olmaz; `call` ile hâlâ çağrılabilir
(bu işlemin ayrıntısı Bağlam ve Kapanışlar konusundadır). Üçüncüsü, kendi özellik
silindiğinde zincir yeniden görünür olur — silme, prototipteki tanımı geri getiriyormuş
gibi görünür ama aslında yalnızca gölgeyi kaldırır.

#### Kendi Özellik ve Devralınan Özellik

Zincir üzerinden gelen özellikler çoğu okuma işleminde kendi özelliklerden ayırt edilmez;
ayrımın önemli olduğu yerler ise sayma ve dizileştirme işlemleridir.

```js
const olcumDavranisi = {
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
};

const kayit = Object.create(olcumDavranisi);
kayit.sensor = "S-01";
kayit.deger = 21.4;
kayit.zaman = 1000;

console.log("bicimle" in kayit);
console.log(Object.hasOwn(kayit, "bicimle"));
console.log(Object.keys(kayit).join(","));

const forInAnahtarlari = [];
for (const anahtar in kayit) {
  forInAnahtarlari.push(anahtar);
}
console.log(forInAnahtarlari.join(","));

console.log(JSON.stringify(kayit));
```

```
true
false
sensor,deger,zaman
sensor,deger,zaman,bicimle
{"sensor":"S-01","deger":21.4,"zaman":1000}
```

`in` işleci zincirin tamamına bakar, `Object.hasOwn` yalnızca nesnenin kendisine.
`Object.keys` kendi özelliklerle sınırlıdır; `for...in` döngüsü ise zincirden gelen
özellikleri de dolaşır ve bu nedenle nesne üzerinde gezinmek için güvenilir bir araç
değildir. `JSON.stringify` de kendi özelliklerle çalışır — dizileştirme yalnızca veriyi
alır, davranışı bırakır. Bu davranış, dersin sonundaki ayrımı önceden haber verir:
**bir nesnenin verisi kendisinde, kimliği zincirindedir.**

Buradaki `bicimle` adının `for...in` çıktısında görünmesi, prototipteki özelliğin
*sayılabilir* olmasından kaynaklanır. Sayılabilirliğin nasıl denetlendiği Özellik
Tanımlayıcıları dersinin konusudur; sınıf sözdiziminin bu konudaki farkı da orada
gösterilecektir.

#### Zincirin Sonu

Zincir sonsuz değildir. Nesne değişmeziyle yaratılan her nesnenin prototipi
`Object.prototype` nesnesidir ve onun prototipi `null` değeridir. `toString`,
`valueOf`, `hasOwnProperty` gibi adların her nesnede bulunmasının nedeni budur.

```js
const olcumDavranisi = {
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
};
const kayit = Object.create(olcumDavranisi);

console.log(Object.getPrototypeOf(kayit) === olcumDavranisi);
console.log(Object.getPrototypeOf(olcumDavranisi) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype));

console.log(typeof kayit.toString);
console.log(typeof kayit.bilinmeyenOzellik);

const sozluk = Object.create(null);
sozluk.sensor = "S-01";
console.log(Object.getPrototypeOf(sozluk));
console.log(typeof sozluk.toString);
console.log("toString" in sozluk);
```

```
true
true
null
function
undefined
null
undefined
false
```

Zincir üç halkadan oluşur: `kayit` → `olcumDavranisi` → `Object.prototype` → `null`.
Var olmayan bir özellik istendiğinde çalışma zamanı bu üç durağı da gezer ve `undefined`
döndürür; hata vermez. Arama maliyeti zincir uzunluğuyla artar, ancak her durakta
yapılan iş bir sözlük aramasıdır — Veri Yapıları kursundaki Karma Tabloları dersinde
incelenen ortalama sabit maliyetli erişimin ta kendisi.

`Object.create(null)` ile üretilen nesnenin zinciri hiç yoktur. Böyle bir nesne saf
sözlük olarak davranır: `toString` gibi devralınan adlar bulunmadığı için, dışarıdan
gelen anahtarların yerleşik adlarla çakışma riski ortadan kalkar. Anahtarları program
dışından gelen eşlemelerde bu kuruluş, `for...in` ve `in` sonuçlarını da öngörülebilir
kılar. Aynı ihtiyacın dildeki asıl karşılığı `Map` türüdür ve İleri Koleksiyonlar
konusunda ele alınır.

#### Özet

- Her nesne, kendi özelliklerine ek olarak bir prototip nesnesine bağ taşır; bağ
  `Object.getPrototypeOf` ile okunur ve doğrudan sınanabilir.
- Özellik araması nesnenin kendisinden başlar, bulunamazsa zincirde yukarı ilerler ve
  `null` durağında `undefined` ile sonuçlanır.
- Yazma işlemi zincire çıkmaz; her zaman nesnenin kendisine yazar ve aynı adlı prototip
  özelliğini gölgeler.
- `in` zincirin tamamına, `Object.hasOwn` ve `Object.keys` yalnızca kendi özelliklere
  bakar; `JSON.stringify` de kendi özelliklerle çalışır.
- Nesne değişmezleriyle kurulan zincir `Object.prototype` üzerinden `null` değerine
  ulaşır; `Object.create(null)` prototipsiz, saf sözlük davranışlı bir nesne üretir.

#### Sonraki Adım

Bu derste prototip elle kuruldu: önce ortak davranış nesnesi yazıldı, sonra her kayıt
`Object.create` ile ona bağlandı. Bu kalıp çalışır ama bir tür tanımına benzemez — yeni
bir kayıt türü türetmek, ortak alanları doldurmak ve iki kayıt türü arasında ortak
davranışı paylaştırmak için tekrar eden bir düzen gerekir. Sonraki ders, nesneden nesneye
kalıtımın yerleşik kalıplarını inceleyecek ve `new` işlecinin bu bağı sizin yerinize nasıl
kurduğunu gösterecek.

### Prototipsel Kalıtım

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/prototipsel-kalitim

Yapıcı işlevler ve new işlecinin kurduğu bağ, prototip zincirini iki katmana çıkarmak, tür sorgulama işlemleri ve bağın çalışma zamanında değişmesi.

Önceki ders prototip bağını elle kurdu: ortak davranış bir nesnede toplandı, her kayıt
`Object.create` ile ona bağlandı, alanlar tek tek dolduruldu. Kalıp işler ama iki
eksiği vardır. Alanların doldurulması ile davranışın bağlanması ayrı yerlerde durur ve
bir kayıt türünden başka bir kayıt türü türetmenin yerleşik bir düzeni yoktur.

Dil, her ikisi için de yerleşik bir yol sunar. Bu ders o yolu — yapıcı işlev ve `new`
işlecini — açar, sonra `new` işlecinin yaptığı işi elle yazarak bunun bir sihir değil,
önceki derste gördüğünüz üç adımın kısaltması olduğunu gösterir.

#### Yapıcı İşlev

JavaScript'te her fonksiyon, `prototype` adlı sıradan bir özellik taşır. Bu özellik
fonksiyonun kendi prototipi **değildir**; `new` ile çağrıldığında üreteceği nesnelerin
prototipi olacak nesnedir. Ad benzerliği bu ayrımı gizlediği için, iki kavramı
karıştırmamak gerekir.

`new` ile çağrılmak üzere yazılan fonksiyona **yapıcı işlev (constructor function)**
denir. Alanları `this` üzerinden doldurur, davranışları ise `prototype` nesnesine
yazılır.

```js
function OlcumKaydi(sensor, deger, zaman) {
  this.sensor = sensor;
  this.deger = deger;
  this.zaman = zaman;
}

OlcumKaydi.prototype.bicimle = function () {
  return `${this.sensor}@${this.zaman}: ${this.deger}`;
};

OlcumKaydi.prototype.esikAstiMi = function (esik) {
  return this.deger > esik;
};

const kayitBir = new OlcumKaydi("S-01", 21.4, 1000);
const kayitIki = new OlcumKaydi("S-02", 19.8, 1060);

console.log(kayitBir.bicimle());
console.log(Object.getPrototypeOf(kayitBir) === OlcumKaydi.prototype);
console.log(kayitBir.bicimle === kayitIki.bicimle);
console.log(Object.keys(kayitBir).join(","));
console.log(kayitBir.constructor === OlcumKaydi);
console.log(Object.hasOwn(OlcumKaydi.prototype, "constructor"));
```

```
S-01@1000: 21.4
true
true
sensor,deger,zaman
true
true
```

Sonuç önceki dersteki kuruluşla aynıdır: veri her kayıtta ayrı, davranış tek yerde.
Dördüncü satır bunu doğrular — kaydın kendi özellikleri yalnızca üç veri alanıdır.

Beşinci ve altıncı satırlar bir ayrıntıyı açığa çıkarır. Her fonksiyonun `prototype`
nesnesi, yaratıldığı anda `constructor` adlı bir özellik taşır ve bu özellik fonksiyonun
kendisini gösterir. `kayitBir.constructor` ifadesi kayıtta bulunmaz; zincirden,
`OlcumKaydi.prototype` üzerinden gelir. Yani `constructor` bir nesnenin "gerçek türü"
değil, zincirinde rastlanan sıradan bir özelliktir — sonraki bölümde bu ayrımın bedeli
görülecektir.

#### new İşlecinin Yaptığı İş

`new OlcumKaydi(...)` ifadesi dört adıma ayrılır:

1. Prototipi `OlcumKaydi.prototype` olan boş bir nesne yaratılır.
2. Yapıcı işlev, `this` bu nesneye bağlanmış olarak çağrılır.
3. Yapıcı işlev bir nesne döndürüyorsa o nesne, döndürmüyorsa ilk adımdaki nesne
   sonuç olur.
4. Sonuç ifadenin değeri olur.

Bu adımların hepsi sıradan işlemlerdir; hiçbirinde dile özel bir yetki yoktur. Aynı işi
yapan bir fonksiyon yazılabilir.

```js
function OlcumKaydi(sensor, deger, zaman) {
  this.sensor = sensor;
  this.deger = deger;
  this.zaman = zaman;
}
OlcumKaydi.prototype.bicimle = function () {
  return `${this.sensor}@${this.zaman}: ${this.deger}`;
};

function yeniOrnek(YapiciIslev, ...argumanlar) {
  const nesne = Object.create(YapiciIslev.prototype);
  const sonuc = YapiciIslev.apply(nesne, argumanlar);
  return typeof sonuc === "object" && sonuc !== null ? sonuc : nesne;
}

const elle = yeniOrnek(OlcumKaydi, "S-01", 21.4, 1000);
const islecle = new OlcumKaydi("S-01", 21.4, 1000);

console.log(elle.bicimle());
console.log(Object.getPrototypeOf(elle) === OlcumKaydi.prototype);
console.log(JSON.stringify(elle) === JSON.stringify(islecle));
console.log(elle instanceof OlcumKaydi);
```

```
S-01@1000: 21.4
true
true
true
```

`yeniOrnek` işlevi `new` işlecinin yerine geçer ve ayırt edilemeyen bir sonuç üretir.
`apply` çağrısı `this` bağlamasını açıkça kurar; bu işlemin kuralları Bağlam ve
Kapanışlar konusunun ilk iki dersinde ele alınacaktır. Buradaki asıl gözlem şudur:
`new`, prototip modelinin üzerine konmuş bir kısaltmadır, ayrı bir mekanizma değil.

Yazdığımız `yeniOrnek` işlevi `new` işlecinin tamamını karşılamaz; sınıf sözdizimiyle
tanımlanmış yapıcılar `new` olmadan çağrılamaz ve `new.target` gibi ayrıntılar bu
kısaltmanın dışında kalır. Model açısından fark eden kısım yukarıdaki üç satırdır.

#### Zinciri Uzatmak

Ölçüm kayıtlarının bir alt türünü ele alalım: sensörün sistematik sapmasını taşıyan
**kalibreli ölçüm**. Alanları ölçüm kaydının alanlarını kapsar, üzerine bir kayma değeri
ekler; okunan değeri hesaplama biçimi farklıdır, biçimlendirme biçimi aynıdır.

Zinciri iki katmana çıkarmak için iki iş gerekir: alt türün prototipi üst türün
prototipine bağlanmalı ve alt yapıcı çalışırken üst yapıcının alan doldurma işi de
yapılmalıdır.

```js
function OlcumKaydi(sensor, deger, zaman) {
  this.sensor = sensor;
  this.deger = deger;
  this.zaman = zaman;
}
OlcumKaydi.prototype.bicimle = function () {
  return `${this.sensor}@${this.zaman}: ${this.deger}`;
};
OlcumKaydi.prototype.okunanDeger = function () {
  return this.deger;
};

function KalibreliOlcum(sensor, deger, zaman, kayma) {
  OlcumKaydi.call(this, sensor, deger, zaman);
  this.kayma = kayma;
}

KalibreliOlcum.prototype = Object.create(OlcumKaydi.prototype);
KalibreliOlcum.prototype.constructor = KalibreliOlcum;

KalibreliOlcum.prototype.okunanDeger = function () {
  return this.deger + this.kayma;
};

const kalibreli = new KalibreliOlcum("S-03", 21.4, 1120, -0.4);

console.log(kalibreli.bicimle());
console.log(kalibreli.okunanDeger());
console.log(OlcumKaydi.prototype.okunanDeger.call(kalibreli));
console.log(Object.getPrototypeOf(kalibreli) === KalibreliOlcum.prototype);
console.log(Object.getPrototypeOf(KalibreliOlcum.prototype) === OlcumKaydi.prototype);
console.log(kalibreli.constructor === KalibreliOlcum);
```

```
S-03@1120: 21.4
21
21.4
true
true
true
```

Zincir dört halkaya çıktı: `kalibreli` → `KalibreliOlcum.prototype` →
`OlcumKaydi.prototype` → `Object.prototype` → `null`. `bicimle` ikinci halkada
bulunmaz, üçüncüde bulunur; `okunanDeger` ikinci halkada bulunduğu için üçüncüdeki
tanımı gölgeler. Programlama Temelleri kursundaki Nesneye Dayalı Programlamaya Giriş
dersinde tanıtılan **çok biçimlilik**, burada tam olarak bu gölgelemedir: aynı ad, zincirin
farklı katmanlarında farklı gerçekleştirimlere karşılık gelir ve hangisinin çalışacağına
arama sırası karar verir.

Üç satır dikkat ister. `OlcumKaydi.call(this, ...)` üst yapıcının alan doldurma işini
yeni nesne üzerinde çalıştırır. `Object.create(OlcumKaydi.prototype)` ile kurulan bağ,
`KalibreliOlcum.prototype = OlcumKaydi.prototype` biçiminde kurulsaydı iki tür aynı
prototip nesnesini paylaşırdı ve alt türe eklenen her davranış üst türe de sızardı.
`constructor` özelliğinin elle geri yazılması ise ilk bölümdeki gözlemin sonucudur:
`prototype` nesnesi değiştirildiğinde `constructor` ile birlikte kaybolur, yeniden
yazılmazsa `kalibreli.constructor` zincirden `OlcumKaydi` değerini bulur.

#### Zincirde Tür Sorgulama

`instanceof` işleci, sağındaki fonksiyonun `prototype` nesnesinin, solundaki nesnenin
zincirinde bulunup bulunmadığını sorar. Sınıf üyeliği gibi görünen bu sorgu, aslında bir
zincir taramasıdır.

```js
function OlcumKaydi(sensor, deger) {
  this.sensor = sensor;
  this.deger = deger;
}
function KalibreliOlcum(sensor, deger, kayma) {
  OlcumKaydi.call(this, sensor, deger);
  this.kayma = kayma;
}
KalibreliOlcum.prototype = Object.create(OlcumKaydi.prototype);
KalibreliOlcum.prototype.constructor = KalibreliOlcum;

const kalibreli = new KalibreliOlcum("S-03", 21.4, -0.4);
const yalin = new OlcumKaydi("S-01", 21.4);

console.log(kalibreli instanceof KalibreliOlcum);
console.log(kalibreli instanceof OlcumKaydi);
console.log(kalibreli instanceof Object);
console.log(yalin instanceof KalibreliOlcum);

console.log(OlcumKaydi.prototype.isPrototypeOf(kalibreli));
console.log(KalibreliOlcum.prototype.isPrototypeOf(yalin));

let durak = kalibreli;
const zincir = [];
while (durak !== null) {
  durak = Object.getPrototypeOf(durak);
  zincir.push(durak === null ? "null" : durak.constructor.name);
}
console.log(zincir.join(" -> "));
```

```
true
true
true
false
true
false
KalibreliOlcum -> OlcumKaydi -> Object -> null
```

Son blok zinciri baştan sona yürüyerek yazdırır ve `instanceof` sonuçlarının nereden
geldiğini görünür kılar. Kalibreli ölçümün zincirinde her üç `prototype` nesnesi de
bulunduğu için üç sorgu da olumlu yanıtlanır; yalın kayıt alt türün prototipini
zincirinde taşımadığı için dördüncü sorgu olumsuzdur. `isPrototypeOf` aynı soruyu ters
yönden sorar ve fonksiyona değil doğrudan prototip nesnesine bakar.

#### Bağın Canlılığı

Prototip bağı bir kopya değil, canlı bir başvurudur. Nesne yaratıldıktan sonra prototipe
eklenen bir özellik, var olan tüm nesnelerde hemen görünür.

```js
function OlcumKaydi(sensor, deger) {
  this.sensor = sensor;
  this.deger = deger;
}
OlcumKaydi.prototype.bicimle = function () {
  return `${this.sensor}: ${this.deger}`;
};

const kayit = new OlcumKaydi("S-01", 21.4);
console.log(kayit.bicimle());
console.log(typeof kayit.ozetle);

OlcumKaydi.prototype.ozetle = function () {
  return `${this.sensor} kaydi`;
};

console.log(typeof kayit.ozetle);
console.log(kayit.ozetle());

const digerDavranis = {
  bicimle() {
    return `[${this.sensor}] ${this.deger}`;
  },
};
Object.setPrototypeOf(kayit, digerDavranis);
console.log(kayit.bicimle());
console.log(kayit instanceof OlcumKaydi);
console.log(typeof kayit.ozetle);
```

```
S-01: 21.4
undefined
function
S-01 kaydi
[S-01] 21.4
false
undefined
```

`ozetle` kaydın yaratılmasından sonra eklenmiştir ve hiçbir yeniden yaratma olmadan
erişilebilir olur; arama her okumada baştan yapıldığı için bağ canlıdır.

`Object.setPrototypeOf` var olan bir nesnenin bağını değiştirir. Sonuçlar geniştir:
kayıt artık üst türün davranışlarını görmez, `instanceof` sonucu tersine döner ve
çalışma zamanları nesne biçimlerine göre eniyileme yaptığı için erişim başarımı düşer.
Bu nedenle bağ, nesnenin yaratıldığı anda kurulur ve sonradan değiştirilmez;
`Object.create` ya da `new` bunun için vardır.

#### Özet

- Her fonksiyonun `prototype` özelliği, `new` ile üreteceği nesnelerin prototipi olacak
  nesnedir; fonksiyonun kendi prototipi değildir.
- `new`, prototipi `prototype` nesnesine bağlı boş bir nesne yaratır, yapıcıyı bu nesne
  üzerinde çalıştırır ve sonucu döndürür; aynı iş `Object.create` ile elle yazılabilir.
- Zinciri uzatmak iki işten oluşur: alt prototipi üst prototipe bağlamak ve üst yapıcıyı
  alt yapıcı içinden çağırmak.
- Alt katmandaki aynı adlı tanım üsttekini gölgeler; çok biçimlilik bu arama sırasının
  sonucudur.
- `instanceof` ve `isPrototypeOf`, tür üyeliğini değil, bir prototip nesnesinin zincirde
  bulunup bulunmadığını sorar.
- Bağ canlıdır: prototipe sonradan eklenen özellikler var olan nesnelerde görünür.

#### Sonraki Adım

Bu derste zincir elle kuruldu ve kuruluşun her adımı görünür oldu: `prototype` ataması,
`constructor` onarımı, üst yapıcı çağrısı. Üç adımın da unutulmaya açık olması, dile
bunları tek bir bildirimde toplayan bir sözdizim eklenmesinin gerekçesidir. Sonraki ders
sınıf sözdizimini tanıtacak ve her satırının bu derste elle yazdığınız hangi işleme
karşılık geldiğini, aynı sınamalarla gösterecek.

### Sınıf Sözdizimi

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/sinif-sozdizimi

Sınıf bildiriminin prototip zincirindeki birebir karşılığı, statik tarafın kalıtımı, yöntemlerin sayılabilirliği ve sözdizimin getirdiği gerçek farklar.

Önceki ders iki katmanlı bir zinciri elle kurdu. Üç ayrı işlem gerekti: alt prototipin üst
prototipe bağlanması, `constructor` özelliğinin onarılması ve üst yapıcının alt yapıcı
içinden çağrılması. Üçünden biri unutulduğunda hata çalışma zamanında, çoğu kez uzak bir
yerde ortaya çıkar.

**Sınıf sözdizimi (class syntax)**, bu üç işlemi tek bir bildirimde toplar. Bu ders, sınıf
bildiriminin her satırının hangi prototip işlemine karşılık geldiğini önceki dersteki
sınamalarla gösterir, sonra sözdizimin gerçekten yeni olan yanlarını ayırır. Ana savı
baştan söylemek gerekir: **sınıf, prototip zincirinin sözdizimsel yüzüdür**; altında
farklı bir nesne modeli yoktur.

#### Aynı Zincir, Tek Bildirim

Ölçüm kaydı ve kalibreli ölçüm, bu kez sınıf bildirimiyle yazılır.

```js
class OlcumKaydi {
  constructor(sensor, deger, zaman) {
    this.sensor = sensor;
    this.deger = deger;
    this.zaman = zaman;
  }

  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  }

  okunanDeger() {
    return this.deger;
  }
}

class KalibreliOlcum extends OlcumKaydi {
  constructor(sensor, deger, zaman, kayma) {
    super(sensor, deger, zaman);
    this.kayma = kayma;
  }

  okunanDeger() {
    return super.okunanDeger() + this.kayma;
  }
}

const kalibreli = new KalibreliOlcum("S-03", 21.4, 1120, -0.4);

console.log(kalibreli.bicimle());
console.log(kalibreli.okunanDeger());
console.log(typeof OlcumKaydi);
console.log(Object.getPrototypeOf(kalibreli) === KalibreliOlcum.prototype);
console.log(Object.getPrototypeOf(KalibreliOlcum.prototype) === OlcumKaydi.prototype);
console.log(kalibreli.constructor === KalibreliOlcum);
console.log(Object.keys(kalibreli).join(","));
```

```
S-03@1120: 21.4
21
function
true
true
true
sensor,deger,zaman,kayma
```

Çıktının ilk iki satırı önceki dersin çıktısıyla aynıdır. Kalan satırlar sözdizimin
kurduğu yapıyı açığa çıkarır:

- `typeof OlcumKaydi` sonucu `"function"` — sınıf bildirimi yeni bir değer türü
  üretmez, bir fonksiyon üretir.
- Örneğin prototipi `KalibreliOlcum.prototype` nesnesidir; `extends`, bu nesnenin
  prototipini `OlcumKaydi.prototype` yapar.
- `constructor` özelliği doğru fonksiyonu gösterir; elle onarım gerekmez.
- Örneğin kendi özellikleri yalnızca dört veri alanıdır. Yöntemler örnekte değil,
  `prototype` nesnesinde durur.

`super.okunanDeger()` çağrısı da zincirle açıklanır: aramayı `this` üzerinden değil, bir
üst prototipten başlatır. Önceki derste bu iş `OlcumKaydi.prototype.okunanDeger.call(this)`
biçiminde elle yazılmıştı; `super` aynı işin adıdır.

#### Statik Taraf

Sınıf bildirimi `static` sözcüğüyle işaretlenen üyeleri örneklere değil, sınıf
fonksiyonunun kendisine yazar. Burada elle kurulan kalıptan gerçek bir fark ortaya çıkar:
`extends` yalnızca `prototype` nesnelerini değil, **sınıf fonksiyonlarını da** birbirine
bağlar.

```js
class OlcumKaydi {
  static olcuBirimi = "santigrat";

  static metinden(satir) {
    const [sensor, deger, zaman] = satir.split(";");
    return new this(sensor, Number(deger), Number(zaman));
  }

  constructor(sensor, deger, zaman) {
    this.sensor = sensor;
    this.deger = deger;
    this.zaman = zaman;
  }

  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  }
}

class KalibreliOlcum extends OlcumKaydi {}

const kayit = OlcumKaydi.metinden("S-01;21.4;1000");
const alt = KalibreliOlcum.metinden("S-03;22.1;1120");

console.log(kayit.bicimle());
console.log(alt.bicimle());
console.log(alt instanceof KalibreliOlcum);
console.log(KalibreliOlcum.olcuBirimi);
console.log(Object.getPrototypeOf(KalibreliOlcum) === OlcumKaydi);
console.log(Object.hasOwn(KalibreliOlcum, "olcuBirimi"));
```

```
S-01@1000: 21.4
S-03@1120: 22.1
true
santigrat
true
false
```

Son iki satır olayı özetler: `KalibreliOlcum` sınıfı `olcuBirimi` özelliğini kendisi
taşımaz, prototipi olan `OlcumKaydi` üzerinden devralır. Yani sınıf bildirimi **iki
zincir** kurar — biri örnekler için `prototype` nesneleri arasında, biri sınıf
fonksiyonları arasında. Önceki dersteki elle kuruluşta ikincisi hiç kurulmuyordu.

`metinden` yönteminin içindeki `new this(...)` ifadesi bu ikinci zincirin sonucudur:
statik yöntem hangi sınıf üzerinden çağrıldıysa `this` o sınıfa bağlanır, bu yüzden alt
sınıftan çağrıldığında alt sınıfın örneği üretilir.

#### Yöntemlerin Sayılabilirliği

İlk derste `for...in` döngüsünün prototipten gelen `bicimle` adını da listelediği
görülmüştü. Sınıf sözdizimiyle tanımlanan yöntemlerde bu olmaz.

```js
class OlcumKaydi {
  constructor(sensor, deger) {
    this.sensor = sensor;
    this.deger = deger;
  }
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  }
}

function EskiOlcumKaydi(sensor, deger) {
  this.sensor = sensor;
  this.deger = deger;
}
EskiOlcumKaydi.prototype.bicimle = function () {
  return `${this.sensor}: ${this.deger}`;
};

const yeni = new OlcumKaydi("S-01", 21.4);
const eski = new EskiOlcumKaydi("S-01", 21.4);

console.log(Object.keys(OlcumKaydi.prototype).join(",") || "(bos)");
console.log(Object.keys(EskiOlcumKaydi.prototype).join(",") || "(bos)");
console.log(Object.getOwnPropertyNames(OlcumKaydi.prototype).join(","));

const yeniAnahtarlar = [];
for (const anahtar in yeni) yeniAnahtarlar.push(anahtar);
const eskiAnahtarlar = [];
for (const anahtar in eski) eskiAnahtarlar.push(anahtar);

console.log(yeniAnahtarlar.join(","));
console.log(eskiAnahtarlar.join(","));
```

```
(bos)
bicimle
constructor,bicimle
sensor,deger
sensor,deger,bicimle
```

Sınıf yöntemleri prototipte **vardır** — üçüncü satır bunu gösterir — ama *sayılamaz*
olarak tanımlanmıştır; bu yüzden `Object.keys` ve `for...in` onları atlar. Elle yazılan
atama ise sayılabilir bir özellik üretir ve döngüye sızar. Sayılabilirliğin ne olduğu ve
elle nasıl denetlendiği sonraki dersin konusudur.

#### Sözdizimin Getirdiği Gerçek Farklar

Sınıf bildirimi yalnızca kısaltma değildir; birkaç davranışı da değiştirir. En görünür
ikisi doğrudan sınanabilir.

```js
function eskiKayit(sensor) {
  this.sensor = sensor;
}
const bildirimdenOnce = new eskiKayit("S-01");
console.log(bildirimdenOnce.sensor);

try {
  new OlcumKaydi("S-01");
} catch (hata) {
  console.log(hata.constructor.name);
}

class OlcumKaydi {
  constructor(sensor) {
    this.sensor = sensor;
  }
}

console.log(new OlcumKaydi("S-01").sensor);

try {
  OlcumKaydi("S-01");
} catch (hata) {
  console.log(hata.constructor.name);
}
```

```
S-01
ReferenceError
S-01
TypeError
```

Birinci fark **yukarı taşımadır**. Fonksiyon bildirimi tanımından önce çağrılabilir;
sınıf bildirimi çağrılamaz, çünkü adı bildirime kadar zamansal ölü bölgededir. JavaScript
Temelleri kursundaki Adlandırma ve Kapsam Kuralları dersinde `let` ve `const` için
anlatılan davranışın aynısıdır.

İkinci fark **çağrı biçimidir**. Yapıcı işlev `new` olmadan çağrılabilir ve bu durumda
`this` beklenmedik bir değere bağlanır; sınıf yapıcısı `new` olmadan çağrıldığında hata
verir. Üçüncü fark çıktıda görünmez: sınıf gövdesindeki tüm kod katı kipte (strict mode)
çalışır, bildirilmemiş değişkene atama gibi sessiz hatalar burada sessiz kalmaz.

#### Özel Alanlar

Elle kurulan kalıpta bir nesnenin iç durumunu gizlemenin dil düzeyinde bir yolu yoktu;
adlandırma anlaşmaları (baştaki alt çizgi) yalnızca niyet bildiriyordu. Sınıf sözdizimi,
adı diyez imiyle başlayan **özel alanları (private fields)** getirir. Bunlar sıradan
özellik değildir — prototip zincirinde de, nesnenin özellik listesinde de görünmezler.

```js
class OlcumKaydi {
  #kayma;

  constructor(sensor, deger, kayma = 0) {
    this.sensor = sensor;
    this.deger = deger;
    this.#kayma = kayma;
  }

  get okunanDeger() {
    return this.deger + this.#kayma;
  }

  kaymaTasiyorMu() {
    return this.#kayma !== 0;
  }

  static kaymaGorunur(nesne) {
    return #kayma in nesne;
  }
}

const kayit = new OlcumKaydi("S-01", 21.4, -0.4);

console.log(kayit.okunanDeger);
console.log(kayit.kaymaTasiyorMu());
console.log(Object.keys(kayit).join(","));
console.log(JSON.stringify(kayit));
console.log(Object.getOwnPropertyNames(kayit).join(","));
console.log(OlcumKaydi.kaymaGorunur(kayit));
console.log(OlcumKaydi.kaymaGorunur({ sensor: "S-02", deger: 19.8 }));
console.log(Object.getOwnPropertyNames(OlcumKaydi.prototype).join(","));
```

```
21
true
sensor,deger
{"sensor":"S-01","deger":21.4}
sensor,deger
true
false
constructor,okunanDeger,kaymaTasiyorMu
```

Özel alan hiçbir listeleme işleminde görünmez ve dizileştirmeye girmez; sınıf gövdesi
dışından erişmeyi denemek çalışma zamanı hatası bile üretmez, kod hiç ayrıştırılamaz.
Erişim, yalnızca alanı bildiren sınıfın gövdesinden mümkündür. `#kayma in nesne` biçimi,
bir nesnenin bu alanı taşıyıp taşımadığını hata yükseltmeden sorar.

Son satır bir ayrıntıyı daha gösterir: `okunanDeger` bir **erişimci (accessor)** olarak
tanımlandığı hâlde prototipte durur. Yani `kayit.okunanDeger` ifadesi bir özellik okuma
gibi görünür ama zincirde bulunan bir fonksiyonu çalıştırır. Erişimcilerin nasıl
tanımlandığı sonraki dersin ikinci konusudur.

Kapsülleme için dilin sunduğu ikinci yol kapanışlardır ve gizlemeyi bambaşka bir
mekanizmayla, prototipe hiç dokunmadan sağlar; ikisinin karşılaştırması Bağlam ve
Kapanışlar konusundaki Kapanışlar dersinde yapılacaktır.

#### Özet

- Sınıf bildirimi bir fonksiyon üretir; örnekler `prototype` nesnesine bağlanır ve
  yöntemler orada durur.
- `extends` iki zincir kurar: örnek prototipleri arasında ve sınıf fonksiyonları
  arasında; ikincisi statik üyelerin devralınmasını sağlar.
- `super`, aramayı bir üst prototipten başlatan ifadedir; elle yazılan `call` çağrısının
  karşılığıdır.
- Sınıf yöntemleri prototipte sayılamaz olarak tanımlanır, bu yüzden `for...in` ve
  `Object.keys` sonuçlarına girmez.
- Sözdizimin gerçek farkları şunlardır: zamansal ölü bölge, `new` zorunluluğu, katı kip ve
  özel alanlar.
- Özel alanlar özellik değildir; listelenmez, dizileştirilmez ve yalnızca bildiren sınıf
  gövdesinden erişilir.

#### Sonraki Adım

Bu derste iki kez sayılabilirliğe çarpıldı: sınıf yöntemleri `for...in` çıktısına
girmiyor, elle yazılan atamalar giriyordu. Bir de erişimci ortaya çıktı — okuma gibi
görünüp fonksiyon çalıştıran özellik. Her ikisi de özelliklerin yalnızca bir değerden
ibaret olmadığını, yanlarında davranışlarını belirleyen bayraklar taşıdığını gösterir.
Sonraki ders bu bayrakları — yazılabilirlik, sayılabilirlik, yapılandırılabilirlik — ve
erişimcileri doğrudan ele alacak.

### Özellik Tanımlayıcıları

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/ozellik-tanimlayicilari

Bir özelliğin değerinin yanında taşıdığı yazılabilirlik, sayılabilirlik ve yapılandırılabilirlik bayrakları; erişimci özellikler ve tanımlayıcıları koruyan kopyalama.

Önceki ders iki kez aynı yere geldi: sınıf yöntemleri `for...in` çıktısında görünmüyor,
elle yazılan atamalar görünüyordu; ve `okunanDeger` bir okuma gibi yazıldığı hâlde
fonksiyon çalıştırıyordu. İkisi de tek bir gerçeğin sonucudur.

Bir özellik yalnızca "ad → değer" çiftinden ibaret değildir. Her özelliğin yanında,
davranışını belirleyen bir **özellik tanımlayıcısı (property descriptor)** durur. Bu ders
tanımlayıcının alanlarını tek tek açar; ardından değer yerine fonksiyon taşıyan
erişimci özellikleri ele alır.

#### Tanımlayıcıyı Okumak

`Object.getOwnPropertyDescriptor`, bir nesnenin **kendi** özelliğinin tanımlayıcısını
döndürür. Veri özelliklerinde dört alan bulunur: `value`, `writable`, `enumerable`,
`configurable`.

```js
function bayraklar(nesne, ad) {
  const tanim = Object.getOwnPropertyDescriptor(nesne, ad);
  if (tanim === undefined) return `${ad}: tanim yok`;
  if ("value" in tanim) {
    return `${ad}: veri, yazilabilir=${tanim.writable}, sayilabilir=${tanim.enumerable}, yapilandirilabilir=${tanim.configurable}`;
  }
  return `${ad}: erisimci, okuyucu=${typeof tanim.get}, yazici=${typeof tanim.set}, sayilabilir=${tanim.enumerable}`;
}

class OlcumKaydi {
  constructor(sensor, deger) {
    this.sensor = sensor;
    this.deger = deger;
  }
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  }
}

const kayit = new OlcumKaydi("S-01", 21.4);
const duzKayit = { sensor: "S-02", deger: 19.8 };

Object.defineProperty(duzKayit, "birim", { value: "santigrat" });

console.log(bayraklar(kayit, "sensor"));
console.log(bayraklar(OlcumKaydi.prototype, "bicimle"));
console.log(bayraklar(duzKayit, "sensor"));
console.log(bayraklar(duzKayit, "birim"));
console.log(bayraklar(kayit, "bicimle"));
```

```
sensor: veri, yazilabilir=true, sayilabilir=true, yapilandirilabilir=true
bicimle: veri, yazilabilir=true, sayilabilir=false, yapilandirilabilir=true
sensor: veri, yazilabilir=true, sayilabilir=true, yapilandirilabilir=true
birim: veri, yazilabilir=false, sayilabilir=false, yapilandirilabilir=false
bicimle: tanim yok
```

Üç kural çıkar. Sıradan atamayla ya da nesne değişmeziyle üretilen özelliklerin üç bayrağı
da açıktır. Sınıf yöntemleri sayılamaz olarak tanımlanır — önceki dersteki `for...in`
farkının kaynağı budur. `Object.defineProperty` çağrısında **belirtilmeyen her bayrak
kapalı sayılır**; bu yüzden `birim` özelliği yazılamaz, sayılamaz ve yapılandırılamaz
çıkmıştır. Son satır ise işlevin adındaki "kendi" sözcüğünü hatırlatır: `bicimle`
prototipte durduğu için kayıt üzerinde tanımı yoktur.

#### Yazılabilirlik

`writable` bayrağı kapalıysa atama değeri değiştirmez. Değiştirmemenin iki farklı biçimi
vardır ve aradaki fark, kodun katı kipte çalışıp çalışmadığına bağlıdır.

```js
const kayit = { sensor: "S-01", deger: 21.4 };

Object.defineProperty(kayit, "birim", {
  value: "santigrat",
  writable: false,
  enumerable: true,
  configurable: false,
});

console.log(kayit.birim);

try {
  kayit.birim = "fahrenhayt";
} catch (hata) {
  console.log(`${hata.constructor.name} yakalandi`);
}
console.log(kayit.birim);

function katiOlmayanYazma(nesne) {
  return new Function("n", "n.birim = 'fahrenhayt'; return n.birim;")(nesne);
}
console.log(katiOlmayanYazma(kayit));

try {
  Object.defineProperty(kayit, "birim", { value: "kelvin" });
} catch (hata) {
  console.log(`${hata.constructor.name} yakalandi`);
}
console.log(kayit.birim);
```

```
santigrat
TypeError yakalandi
santigrat
santigrat
TypeError yakalandi
santigrat
```

Modül dosyaları ve sınıf gövdeleri katı kipte çalışır; orada yazılamayan özelliğe atama
hata yükseltir. `new Function` ile üretilen gövde katı kipte değildir, bu yüzden aynı
atama sessizce başarısız olur — dördüncü satırdaki değişmemiş değer bunu gösterir. Sessiz
başarısızlık, hatanın atamadan çok sonra fark edilmesine yol açtığı için katı kip
tercih edilir.

Son iki satır `configurable` bayrağının etkisidir. Yapılandırılabilirlik kapatıldığında
özellik silinemez ve bayrakların çoğu bir daha değiştirilemez; `writable` hâlâ açıksa
değer değişmeye devam eder ve bu bayrak yalnızca kapatılabilir. `configurable`'ın kendisi
geri alınamayan tek bayraktır. Bu nedenle `configurable: false` bilinçli bir karar
olmalıdır.

#### Sayılabilirlik

`enumerable` bayrağı, özelliğin listelemelere girip girmeyeceğini belirler. Etkisi geniş
bir işlem kümesine yayılır.

```js
const kayit = { sensor: "S-01", deger: 21.4 };

Object.defineProperty(kayit, "kaynakDosya", {
  value: "olcum-2.csv",
  writable: true,
  enumerable: false,
  configurable: true,
});

console.log(kayit.kaynakDosya);
console.log(Object.keys(kayit).join(","));
console.log(Object.getOwnPropertyNames(kayit).join(","));
console.log(JSON.stringify(kayit));
console.log(JSON.stringify({ ...kayit }));
console.log(JSON.stringify(Object.assign({}, kayit)));
console.log("kaynakDosya" in kayit);
console.log(Object.entries(kayit).length);
```

```
olcum-2.csv
sensor,deger
sensor,deger,kaynakDosya
{"sensor":"S-01","deger":21.4}
{"sensor":"S-01","deger":21.4}
{"sensor":"S-01","deger":21.4}
true
2
```

Özellik okunabilir durumdadır ve `in` işleci onu görür; ama `Object.keys`,
`Object.entries`, `JSON.stringify`, nesne yayma ve `Object.assign` onu atlar.
`Object.getOwnPropertyNames` ise sayılabilirliğe bakmaz, bu yüzden gizli kalan
özellikleri denetlemenin yolu odur.

Buradan pratik bir kural çıkar: **veriye eşlik eden ama verinin parçası olmayan
bilgiler** — kaynak dosya adı, iç sayaçlar, önbellek alanları — sayılamaz tanımlanırsa
dizileştirme ve kopyalama işlemlerine kendiliğinden karışmaz. Aynı sonucu simge (symbol)
anahtarlı özellikler de verir; onlar da `Object.keys` ve `JSON.stringify` çıktılarına
girmez.

#### Erişimci Özellikler

İkinci tanımlayıcı türünde `value` ve `writable` yerine `get` ve `set` fonksiyonları
bulunur. Böyle bir özelliğe **erişimci (accessor)** denir: okuma `get` fonksiyonunu,
yazma `set` fonksiyonunu çalıştırır.

Erişimcinin değeri, hesaplanan bir büyüklüğü özellik gibi sunmasıdır. Ölçüm kaydında
okunan değer, ham değer ile kalibrasyon kaymasının toplamıdır; bunu bir yöntem yerine
erişimci yapmak, çağıranı hesabın varlığından habersiz bırakır.

```js
const olcumDavranisi = {};

Object.defineProperty(olcumDavranisi, "okunanDeger", {
  get() {
    return this.deger + this.kayma;
  },
  set(yeniDeger) {
    if (typeof yeniDeger !== "number" || Number.isNaN(yeniDeger)) {
      throw new TypeError("okunan deger sayi olmalidir");
    }
    this.deger = yeniDeger - this.kayma;
  },
  enumerable: false,
  configurable: true,
});

function olcumKaydi(sensor, deger, kayma) {
  const kayit = Object.create(olcumDavranisi);
  kayit.sensor = sensor;
  kayit.deger = deger;
  kayit.kayma = kayma;
  return kayit;
}

const kayit = olcumKaydi("S-01", 21.4, -0.4);

console.log(kayit.okunanDeger);
kayit.okunanDeger = 25;
console.log(kayit.deger);
console.log(kayit.okunanDeger);

try {
  kayit.okunanDeger = "yirmi";
} catch (hata) {
  console.log(`${hata.constructor.name}: ${hata.message}`);
}

console.log(Object.hasOwn(kayit, "okunanDeger"));
console.log(Object.hasOwn(olcumDavranisi, "okunanDeger"));
console.log(JSON.stringify(kayit));
```

```
21
25.4
25
TypeError: okunan deger sayi olmalidir
false
true
{"sensor":"S-01","deger":25.4,"kayma":-0.4}
```

Erişimci prototipte tanımlıdır; kaydın kendi özelliği değildir. Buna karşın `get` ve
`set` gövdelerindeki `this`, erişimin yapıldığı nesneye — yani kaydın kendisine —
bağlanır. Zincirde bulunan bir fonksiyon, çağrıldığı nesnenin verisiyle çalışır; bu
kuralın tamamı sonraki konunun ilk dersinde ele alınacaktır.

Yazıcı fonksiyon, Programlama Temelleri kursundaki Nesneye Dayalı Programlamaya Giriş
dersinde tanıtılan **sınıf değişmezini** korumanın doğrudan yoludur: geçersiz değer
nesneye hiç girmez. Ayrıca erişimci sayılamaz tanımlandığı için dizileştirmeye girmez;
son satırda yalnızca ham veri alanları görünür. Bu, hesaplanmış değerlerin depolanan
veriyle karışmamasını sağlar.

Sınıf gövdesindeki `get` ve `set` sözcükleri bu tanımın kısaltmasıdır; ürettikleri
erişimci de `prototype` nesnesine, sayılamaz olarak yazılır.

#### Tanımlayıcıları Koruyan Kopyalama

Bir nesneyi kopyalamanın alışılmış yolları tanımlayıcıları taşımaz. Nesne yayma ve
`Object.assign`, kaynaktaki her özelliği **okur** ve hedefe sıradan bir veri özelliği
olarak **yazar**. Erişimciler bu işlemde donmuş değere dönüşür.

```js
const kaynak = {
  sensor: "S-01",
  deger: 21.4,
  get etiket() {
    return `${this.sensor}/${this.deger}`;
  },
};

const yayilmis = { ...kaynak };
const tanimlaKopya = Object.defineProperties(
  Object.create(Object.getPrototypeOf(kaynak)),
  Object.getOwnPropertyDescriptors(kaynak),
);

console.log(kaynak.etiket);
console.log(yayilmis.etiket);
console.log(tanimlaKopya.etiket);

kaynak.deger = 30;
yayilmis.deger = 30;
tanimlaKopya.deger = 30;

console.log(kaynak.etiket);
console.log(yayilmis.etiket);
console.log(tanimlaKopya.etiket);

console.log(typeof Object.getOwnPropertyDescriptor(yayilmis, "etiket").get);
console.log(typeof Object.getOwnPropertyDescriptor(tanimlaKopya, "etiket").get);
```

```
S-01/21.4
S-01/21.4
S-01/21.4
S-01/30
S-01/21.4
S-01/30
undefined
function
```

Yayma ile üretilen kopyada `etiket` artık hesaplanmaz; kopyalama anındaki metni taşıyan
sabit bir veri özelliğidir, bu yüzden `deger` değiştiğinde eskisini göstermeye devam eder.
`Object.getOwnPropertyDescriptors` ile `Object.defineProperties` birlikte kullanıldığında
hem erişimciler hem de bayraklar korunur; `Object.create` çağrısı da prototip bağını
taşır. Kopyalamanın genel sorunu — hangi bilginin taşındığı, hangisinin düştüğü — kursun
son dersinde bütünüyle ele alınacaktır.

#### Özet

- Her özellik, değerinin yanında `writable`, `enumerable`, `configurable` bayraklarını
  taşır; `Object.getOwnPropertyDescriptor` bunları okur.
- `Object.defineProperty` çağrısında belirtilmeyen bayraklar kapalı sayılır; atama ile
  üretilen özelliklerde ise üçü de açıktır.
- Yazılamayan özelliğe atama katı kipte hata yükseltir, katı olmayan kipte sessizce
  başarısız olur; `configurable: false` geri alınamaz.
- Sayılamaz özellikler `Object.keys`, `Object.entries`, `JSON.stringify`, yayma ve
  `Object.assign` sonuçlarına girmez; `Object.getOwnPropertyNames` ile görünür.
- Erişimci özellikte okuma ve yazma birer fonksiyon çalıştırır; yazıcı, geçersiz değerin
  nesneye girmesini engelleyerek sınıf değişmezini korur.
- Yayma ve `Object.assign` tanımlayıcıları taşımaz; erişimciyi sabit değere çevirir.

#### Sonraki Adım

`writable: false` tek bir özelliği kilitler. Bir nesnenin tamamının değişmesini önlemek,
ona yeni özellik eklenmesini yasaklamak ya da yalnızca var olan alanların değerini
değiştirilebilir bırakmak için özellik başına tanım yazmak zahmetlidir. Sonraki ders
nesnenin bütününe uygulanan dondurma ve mühürleme işlemlerini, bunların ne kadar derine
indiğini ve değişiklik gerektiğinde yeni nesne üretmenin kalıplarını ele alacak.

### Değişmezlik Teknikleri

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/degismezlik-teknikleri

Dondurma, mühürleme ve genişletmeyi engelleme işlemlerinin kapsamı, sığ kalmalarının sonuçları, derin dondurma ve değişiklik yerine yeni nesne üretme kalıpları.

Önceki ders tek bir özelliği `writable: false` ile kilitledi. Bir nesnenin bütününü
korumak için her alana ayrı tanım yazmak zahmetlidir ve yeni alan eklenmesini de
engellemez. Dil, nesne düzeyinde çalışan üç işlem sunar.

Bu dersin konusu **değiştirilemezlik (immutability)**: bir nesnenin yaratıldıktan sonra
içeriğinin değişmemesi. Terim, önceki derste geçen *sınıf değişmezi* (invariant) ile
karıştırılmamalıdır; değişmez bir kuraldır, değiştirilemezlik ise bir yetenektir.
Programlama Temelleri kursundaki Fonksiyonel Programlamaya Giriş dersi bu yeteneğin
gerekçesini kurmuştu: değeri değişmeyen bir nesne, kaç yerden başvurulursa başvurulsun
sürprizsizdir.

#### Dondurma

`Object.freeze` bir nesnenin tüm kendi özelliklerini yazılamaz ve yapılandırılamaz yapar,
üstelik nesneye yeni özellik eklenmesini de yasaklar.

```js
const kayit = Object.freeze({
  sensor: "S-01",
  deger: 21.4,
  zaman: 1000,
});

console.log(Object.isFrozen(kayit));
console.log(Object.isExtensible(kayit));

const denemeler = [
  ["var olan alani yazma", () => { kayit.deger = 30; }],
  ["yeni alan ekleme", () => { kayit.birim = "santigrat"; }],
  ["alan silme", () => { delete kayit.zaman; }],
  ["tanimi degistirme", () => { Object.defineProperty(kayit, "deger", { value: 30 }); }],
  ["prototip degistirme", () => { Object.setPrototypeOf(kayit, null); }],
];

for (const [ad, islem] of denemeler) {
  try {
    islem();
    console.log(`${ad}: gecti`);
  } catch (hata) {
    console.log(`${ad}: ${hata.constructor.name}`);
  }
}

console.log(JSON.stringify(kayit));
const tanim = Object.getOwnPropertyDescriptor(kayit, "deger");
console.log(`yazilabilir=${tanim.writable}, yapilandirilabilir=${tanim.configurable}`);
```

```
true
false
var olan alani yazma: TypeError
yeni alan ekleme: TypeError
alan silme: TypeError
tanimi degistirme: TypeError
prototip degistirme: TypeError
{"sensor":"S-01","deger":21.4,"zaman":1000}
yazilabilir=false, yapilandirilabilir=false
```

Beş değiştirme yolu da kapanmıştır; prototip bağı bile sabitlenir. Bu ders bir modül
dosyasında, yani katı kipte çalıştığı için denemeler hata yükseltir. Katı olmayan kipte
aynı işlemler sessizce başarısız olur — önceki dersteki ayrımın aynısı.

Dondurma geri alınamaz. `Object.unfreeze` gibi bir işlem yoktur, çünkü `Object.freeze`
`writable` ile `configurable` bayraklarını birlikte kapatır; yapılandırılabilirlik bir
kez kapandıktan sonra ikisi de geri açılamaz. Bir nesneyi
dondurmak, o nesne üzerinde verilmiş nihai bir karardır.

#### Dondurma Sığdır

`Object.freeze` yalnızca nesnenin **kendi** özelliklerine dokunur. İçerideki nesneler ve
prototipteki tanımlar kapsam dışında kalır.

```js
const olcumDavranisi = {
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  },
};

const kayit = Object.create(olcumDavranisi);
kayit.sensor = "S-01";
kayit.deger = 21.4;
kayit.kaynak = { dosya: "olcum-2.csv", satir: 17 };
Object.freeze(kayit);

console.log(Object.isFrozen(kayit));
console.log(Object.isFrozen(kayit.kaynak));

kayit.kaynak.satir = 99;
console.log(kayit.kaynak.satir);

olcumDavranisi.bicimle = function () {
  return `[degistirildi] ${this.sensor}`;
};
console.log(kayit.bicimle());
console.log(Object.isFrozen(Object.getPrototypeOf(kayit)));
```

```
true
false
99
[degistirildi] S-01
false
```

İki sızıntı vardır. Birincisi tanıdıktır: kaydın `kaynak` alanı bir başvurudur; donan şey
başvurunun kendisidir, gösterdiği nesne değil. Programlama Temelleri kursundaki Değer ve
Referans ile Aktarım dersindeki sığ kopya tartışmasının aynısıdır.

İkincisi prototiple ilgilidir ve bu kursa özgüdür. Kaydın *prototip bağı* dondurulmuştur
ama *prototip nesnesi* dondurulmamıştır. Zincirdeki bir davranışın değiştirilmesi,
dondurulmuş nesnenin davranışını değiştirir. Nesnenin verisi kadar davranışının da sabit
kalması isteniyorsa prototip nesnesi de ayrıca dondurulmalıdır.

#### Üç İşlemin Kapsamı

Dondurma en katı seçenektir; iki gevşek biçimi vardır. `Object.preventExtensions`
yalnızca yeni özellik eklenmesini yasaklar, `Object.seal` buna silmeyi de ekler. Farkı
görmek için üçünü aynı denemelerden geçirmek yeterlidir.

```js
function denetle(ad, nesne) {
  const sonuclar = [];
  try {
    nesne.deger = 30;
    sonuclar.push(`yazma=${nesne.deger === 30 ? "oldu" : "olmadi"}`);
  } catch {
    sonuclar.push("yazma=hata");
  }
  try {
    nesne.birim = "santigrat";
    sonuclar.push(`ekleme=${Object.hasOwn(nesne, "birim") ? "oldu" : "olmadi"}`);
  } catch {
    sonuclar.push("ekleme=hata");
  }
  try {
    delete nesne.sensor;
    sonuclar.push(`silme=${Object.hasOwn(nesne, "sensor") ? "olmadi" : "oldu"}`);
  } catch {
    sonuclar.push("silme=hata");
  }
  console.log(`${ad}: ${sonuclar.join(", ")}`);
}

denetle("acik      ", { sensor: "S-01", deger: 21.4 });
denetle("genisletme", Object.preventExtensions({ sensor: "S-01", deger: 21.4 }));
denetle("muhurlu   ", Object.seal({ sensor: "S-01", deger: 21.4 }));
denetle("donmus    ", Object.freeze({ sensor: "S-01", deger: 21.4 }));
```

```
acik      : yazma=oldu, ekleme=oldu, silme=oldu
genisletme: yazma=oldu, ekleme=hata, silme=oldu
muhurlu   : yazma=oldu, ekleme=hata, silme=hata
donmus    : yazma=hata, ekleme=hata, silme=hata
```

Kapsamlar iç içedir: genişletmeyi engelleme ⊂ mühürleme ⊂ dondurma. Mühürleme, alan
kümesi sabit ama değerleri değişebilen nesneler için uygundur — bir yapılandırma kaydının
biçimini kilitleyip içeriğini serbest bırakmak gibi. Dondurma ise nesneyi tümüyle sabit
kılar.

#### Derin Dondurma

Sığlığı aşmak için özyinelemeli bir gezinti gerekir. Programlama Temelleri kursundaki
Özyineleme dersinin kalıbı doğrudan uygulanır: taban durumu nesne olmayan değerlerdir,
indirgeme adımı her alanın kendisini dondurmaktır. Çevrimsel başvuruların sonsuz
özyinelemeye yol açmaması için ziyaret edilenler işaretlenir.

```js
function derinDondur(nesne, gorulen = new WeakSet()) {
  if (nesne === null || typeof nesne !== "object" || gorulen.has(nesne)) {
    return nesne;
  }
  gorulen.add(nesne);
  for (const ad of Object.getOwnPropertyNames(nesne)) {
    const tanim = Object.getOwnPropertyDescriptor(nesne, ad);
    if ("value" in tanim) derinDondur(tanim.value, gorulen);
  }
  return Object.freeze(nesne);
}

const kayit = derinDondur({
  sensor: "S-01",
  deger: 21.4,
  kaynak: { dosya: "olcum-2.csv", satir: 17 },
  etiketler: ["ham", "sicaklik"],
});

console.log(Object.isFrozen(kayit));
console.log(Object.isFrozen(kayit.kaynak));
console.log(Object.isFrozen(kayit.etiketler));

try {
  kayit.kaynak.satir = 99;
} catch (hata) {
  console.log(`ic nesne yazma: ${hata.constructor.name}`);
}
try {
  kayit.etiketler.push("yeni");
} catch (hata) {
  console.log(`diziye ekleme: ${hata.constructor.name}`);
}

console.log(JSON.stringify(kayit));
```

```
true
true
true
ic nesne yazma: TypeError
diziye ekleme: TypeError
{"sensor":"S-01","deger":21.4,"kaynak":{"dosya":"olcum-2.csv","satir":17},"etiketler":["ham","sicaklik"]}
```

Diziler de nesne olduğu için aynı işlemden geçer; dondurulmuş bir diziye ekleme yapmak
hata yükseltir. `getOwnPropertyNames` kullanılması sayılamaz alanların da kapsanmasını
sağlar; erişimci özelliklerde `value` alanı bulunmadığından okuyucu fonksiyon
çalıştırılmaz. `WeakSet` seçimi bilinçlidir: işaretleme geçicidir ve gezilen nesnelerin
ömrünü uzatmamalıdır. Bu koleksiyonun ayrıntısı İleri Koleksiyonlar konusundadır.

Derin dondurmanın maliyeti nesne sayısıyla doğru orantılıdır; büyük ağaçlarda her
yaratımda uygulanması pahalıdır. Yaygın uygulama, dondurmayı yalnızca paylaşılan ve uzun
ömürlü verilerde kullanmaktır.

#### Değiştirmek Yerine Üretmek

Nesne dondurulduğunda "alanı güncelle" isteği bir soruya dönüşür: güncellenmiş yeni nesne
nasıl üretilir? Nesne yayma bunun için yeterli görünür, ama zincirle ilgili bir bedeli
vardır.

```js
class OlcumKaydi {
  constructor(sensor, deger, zaman) {
    this.sensor = sensor;
    this.deger = deger;
    this.zaman = zaman;
    Object.freeze(this);
  }
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  }
  degerleDegistir(yeniDeger) {
    const kopya = Object.create(Object.getPrototypeOf(this));
    Object.assign(kopya, this, { deger: yeniDeger });
    return Object.freeze(kopya);
  }
}

const ilk = new OlcumKaydi("S-01", 21.4, 1000);
const yayilarak = { ...ilk, deger: 30 };
const yontemle = ilk.degerleDegistir(30);

console.log(ilk.bicimle());
console.log(JSON.stringify(yayilarak));
console.log(typeof yayilarak.bicimle);
console.log(Object.getPrototypeOf(yayilarak) === OlcumKaydi.prototype);
console.log(Object.isFrozen(yayilarak));

console.log(yontemle.bicimle());
console.log(Object.getPrototypeOf(yontemle) === OlcumKaydi.prototype);
console.log(yontemle instanceof OlcumKaydi);
console.log(Object.isFrozen(yontemle));
console.log(ilk.deger);
```

```
S-01@1000: 21.4
{"sensor":"S-01","deger":30,"zaman":1000}
undefined
false
false
S-01@1000: 30
true
true
true
21.4
```

Yayma ile üretilen nesne doğru veriyi taşır ama **zincirini kaybetmiştir**: prototipi
`Object.prototype` olur, `bicimle` bulunamaz, `instanceof` sonucu olumsuzdur ve nesne
donmuş değildir. Kursun ilk dersindeki cümle burada karşılığını bulur — kopyalama kendi
özellikleri taşır, zinciri taşımaz.

`degerleDegistir` yöntemi zinciri `Object.create(Object.getPrototypeOf(this))` ile
kurduğu için sonuç gerçek bir ölçüm kaydıdır. Yapıcı yerine mevcut prototipin
kullanılması ayrıca bir kazanç sağlar: alt sınıflardan çağrıldığında alt sınıfın
prototipi korunur.

Son satır kalıbın amacını doğrular: ilk kayıt değişmemiştir. Değişiklik istendiğinde eski
nesne yerinde kalır, yenisi üretilir. Bu, Programlama Temelleri kursundaki Fonksiyonel
Programlamaya Giriş dersinde tanıtılan değiştirilemezlik yaklaşımının nesne modelindeki
karşılığıdır.

#### Özet

- `Object.freeze` kendi özellikleri yazılamaz ve yapılandırılamaz yapar, yeni özellik
  eklemeyi ve prototip değiştirmeyi yasaklar; geri alınamaz.
- Dondurma sığdır: iç nesneler ve prototip nesnesi etkilenmez, bu yüzden davranış hâlâ
  değiştirilebilir.
- Kapsamlar iç içedir — genişletmeyi engelleme, mühürleme, dondurma; ortadaki seçenek
  alan kümesini sabitler, değerleri serbest bırakır.
- Derin dondurma özyinelemeli bir gezintidir; çevrimlere karşı ziyaret işaretlemesi
  gerekir ve maliyeti nesne sayısıyla artar.
- Dondurulmuş nesnede güncelleme, yeni nesne üretmeye dönüşür; nesne yayma zinciri
  kaybettiği için prototip açıkça kurulmalıdır.

#### Sonraki Adım

Bu konu boyunca yöntemler hep `this` üzerinden çalıştı: `bicimle` sensörü kayıttan,
erişimci ham değeri nesnenin kendisinden okudu. Ancak `this` değerinin ne olacağı
fonksiyonun nerede tanımlandığına değil, **nasıl çağrıldığına** bağlıdır — zincirden gelen
bir fonksiyon, hiç tanımadığı bir nesne üzerinde de çalışabilir. Sonraki konu bu bağlanma
kurallarını dört başlık altında ayırt edecek ve yöntemin nesnesinden koptuğu durumları
inceleyecek.

## Bağlam ve Kapanışlar

this bağlanmasının dört kuralı, açık bağlama işlemleri, ok fonksiyonlarında sözcüksel bağlam, kapanışlarla durum taşıma ve fonksiyonu değer olarak kullanma.

### this Bağlanması

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/this-baglanmasi

Bağlamın çağrı yerinde belirlenmesi; genel, örtülü, açık ve yapıcı bağlanma kuralları, aralarındaki öncelik sırası ve yöntemin nesnesinden kopması.

Prototip Modeli konusu boyunca yöntemler `this` üzerinden çalıştı ve bu, sanki fonksiyonun
tanımlandığı nesneyi bildiği izlenimini verdi. İzlenim yanlıştır. Önceki derste
`Object.assign(kopya, this, ...)` çağrısında olduğu gibi, prototipteki bir fonksiyon
zincirinde hiç bulunmadığı bir nesne üzerinde de çalışabilir.

Kural şudur: `this` değeri fonksiyonun **tanımlandığı yere** değil, **çağrıldığı biçime**
bakılarak belirlenir. Aynı fonksiyon, dört farklı çağrı biçiminde dört farklı değere
bağlanır. Bu ders dördünü ayırt eder ve aralarındaki öncelik sırasını kurar.

#### Genel Bağlanma

En yalın çağrı biçimi, fonksiyonun hiçbir nesneye iliştirilmeden çağrılmasıdır. Katı kipte
`this` değeri `undefined` olur.

```js
function bagiOku() {
  return this;
}

console.log(bagiOku() === undefined);
console.log(typeof globalThis);

const katiOlmayanBagiOku = new Function("return this;");
console.log(katiOlmayanBagiOku() === globalThis);
console.log(katiOlmayanBagiOku() === undefined);
```

```
true
object
true
false
```

Katı olmayan kipte aynı çağrı `this` değerini genel nesneye bağlar; `globalThis` bu
nesnenin standart adıdır. Fark önemlidir, çünkü yanlışlıkla nesnesiz çağrılan bir yöntem
katı kipte hemen hata verirken, katı olmayan kipte genel nesneye sessizce özellik yazar.
Modül dosyaları ve sınıf gövdeleri katı kipte çalıştığı için, ölçüm kaydı örneklerinin
tamamında geçerli olan davranış birincisidir.

#### Örtülü Bağlanma

Fonksiyon bir nesnenin özelliği üzerinden çağrılırsa `this`, o nesneye bağlanır. Belirleyen
şey çağrı yerindeki nokta işlecinin **solundaki** nesnedir.

```js
const kayit = {
  sensor: "S-01",
  deger: 21.4,
  zaman: 1000,
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
};

console.log(kayit.bicimle());

const kopukBicimle = kayit.bicimle;
try {
  console.log(kopukBicimle());
} catch (hata) {
  console.log(`kopuk cagri: ${hata.constructor.name}`);
}

const kayitlar = [kayit];
try {
  console.log(kayitlar.map(kayit.bicimle)[0]);
} catch (hata) {
  console.log(`geri cagri: ${hata.constructor.name}`);
}

console.log(kayitlar.map((k) => k.bicimle())[0]);

const olcum = {
  sensor: "S-DIS",
  kaynak: {
    sensor: "S-IC",
    bicimle() {
      return `bag: ${this.sensor}`;
    },
  },
};
console.log(olcum.kaynak.bicimle());
```

```
S-01@1000: 21.4
kopuk cagri: TypeError
geri cagri: TypeError
S-01@1000: 21.4
bag: S-IC
```

İkinci ve üçüncü satırlar dilin en sık karşılaşılan tuzağını gösterir. `kayit.bicimle`
ifadesi bir fonksiyon **değeri** verir; nesneyle bağı bu değerin içinde taşınmaz. Değer
bir değişkene atandığında ya da geri çağrı olarak aktarıldığında çağrı yeri değişir ve
bağlanma genel kurala düşer. Katı kipte `this` `undefined` olduğu için `this.sensor`
okuması hata yükseltir.

Bu, Programlama Temelleri kursundaki Değer ve Referans ile Aktarım dersindeki gözlemin
bir sonucudur: aktarılan şey fonksiyonun kendisidir, bulunduğu bağlam değil. Dördüncü
satırdaki çözüm en dolaysızıdır — çağrıyı nesne üzerinden yapan bir sarmalayıcı yazmak.

Son satır zincir uzunluğunun rol oynamadığını gösterir: yalnızca **son** halka sayılır.
`olcum.kaynak.bicimle()` çağrısında `this`, `kaynak` nesnesidir; dıştaki `olcum` hiç
dikkate alınmaz.

#### Açık Bağlanma

`call`, `apply` ve `bind` işlemleri bağlamı çağrı yerinde açıkça belirtir. Ayrıntıları
sonraki dersin konusudur; burada yalnızca kuralın bir üçüncü biçim olduğu gösterilir.

```js
const olcumDavranisi = {
  bicimle() {
    return `${this.sensor}@${this.zaman}: ${this.deger}`;
  },
};

const zincirdeki = Object.create(olcumDavranisi);
zincirdeki.sensor = "S-01";
zincirdeki.deger = 21.4;
zincirdeki.zaman = 1000;

const yabanci = { sensor: "S-99", deger: 5.5, zaman: 7 };

console.log(zincirdeki.bicimle());
console.log(olcumDavranisi.bicimle.call(yabanci));
console.log(zincirdeki.bicimle.call(yabanci));
console.log(Object.getPrototypeOf(yabanci) === olcumDavranisi);
```

```
S-01@1000: 21.4
S-99@7: 5.5
S-99@7: 5.5
false
```

Son satır dersin başındaki savı doğrular: `yabanci` nesnesinin zincirinde ölçüm davranışı
yoktur, buna karşın aynı fonksiyon onun verisiyle sorunsuz çalışır. Prototip zinciri
fonksiyonun **nerede bulunacağını** belirler; `this` ise **hangi veri üzerinde
çalışacağını**. İkisi birbirinden bağımsızdır.

#### Yapıcı Bağlanma

`new` işleci, ikinci derste sıralanan adımların ikincisinde `this` değerini yeni yaratılan
nesneye bağlar.

```js
function OlcumKaydi(sensor, deger) {
  this.sensor = sensor;
  this.deger = deger;
}
OlcumKaydi.prototype.bicimle = function () {
  return `${this.sensor}: ${this.deger}`;
};

const yapiciyla = new OlcumKaydi("S-01", 21.4);
console.log(yapiciyla.bicimle());
console.log(Object.getPrototypeOf(yapiciyla) === OlcumKaydi.prototype);

try {
  OlcumKaydi("S-02", 19.8);
} catch (hata) {
  console.log(`new olmadan: ${hata.constructor.name}`);
}

class SinifKaydi {
  constructor(sensor) {
    this.sensor = sensor;
  }
  bicimle() {
    return `sinif: ${this.sensor}`;
  }
}

const ornek = new SinifKaydi("S-03");
console.log(ornek.bicimle());

const kopuk = ornek.bicimle;
try {
  kopuk();
} catch (hata) {
  console.log(`sinif yontemi kopuk: ${hata.constructor.name}`);
}
```

```
S-01: 21.4
true
new olmadan: TypeError
sinif: S-03
sinif yontemi kopuk: TypeError
```

Üçüncü satır, yapıcı işlevin `new` olmadan çağrılmasının katı kipte nasıl sonuçlandığını
gösterir: `this` `undefined` olduğu için ilk atama hata yükseltir. Sınıf sözdiziminin bu
çağrıyı baştan yasaklaması, hatayı gövdenin ortasında beklemek yerine çağrının kendisinde
vermek içindir.

Son satır önemli bir noktayı yineler: sınıf sözdizimi kopma sorununu çözmez. Sınıf
yöntemleri de prototipte duran sıradan fonksiyonlardır ve nesnelerinden ayrıldıklarında
aynı biçimde bağlarını yitirirler.

#### Geri Çağırmada Bağlam Kaybı

Dört kural, hataların çıktığı yeri de açıklar. Bir yöntem, gövdesinde başka bir işleve
fonksiyon geçirdiğinde o fonksiyon **ayrı bir çağrı** olur; dış yöntemin bağlamı ona
taşınmaz.

```js
const toplayici = {
  birim: "C",
  olcumler: [21.4, 22.8, 20.1],
  bicimliListe() {
    return this.olcumler.map(function (deger) {
      return `${deger}${this.birim}`;
    });
  },
  bicimliListeThisArg() {
    return this.olcumler.map(function (deger) {
      return `${deger}${this.birim}`;
    }, this);
  },
};

try {
  console.log(toplayici.bicimliListe());
} catch (hata) {
  console.log("geri çağırmada:", hata.constructor.name, "-", hata.message);
}

console.log(toplayici.bicimliListeThisArg());
```

```
geri çağırmada: TypeError - Cannot read properties of undefined (reading 'birim')
[ '21.4C', '22.8C', '20.1C' ]
```

Birinci çağrıda geri çağırma işlevi genel bağlanmaya düşer; katı kipte `this` tanımsız
olduğu için özellik okuması hata verir. Dizi yöntemlerinin bir bölümü ikinci bir
parametre alır ve geri çağırmayı verilen nesneye bağlar; ikinci çağrı bunu kullanır.

Bu parametre her arayüzde bulunmaz. Genel çözüm iki adımdan birindedir: bağlamı açık
bağlanmayla sabitlemek (sonraki ders) ya da kendi `this` bağını kurmayan bir fonksiyon
biçimi kullanmak (üçüncü ders). Sorunun kaynağı, çağrı yerinin fonksiyonun tanımlandığı
yerden bağımsız olmasıdır.

#### Öncelik Sırası

Birden fazla kural aynı anda geçerli göründüğünde hangisinin kazandığı sabittir: yapıcı
bağlanma, açık bağlanmayı; açık bağlanma, örtülü bağlanmayı; örtülü bağlanma, genel
bağlanmayı geçersiz kılar.

```js
function sensoruYaz(sensor) {
  this.sensor = sensor;
  return this;
}

const hedef = { sensor: "baslangic" };
const digerHedef = { sensor: "diger" };

const bagli = sensoruYaz.bind(hedef);

bagli("acik-bagli");
console.log(hedef.sensor);

digerHedef.calistir = bagli;
digerHedef.calistir("ortulu-denemesi");
console.log(hedef.sensor);
console.log(digerHedef.sensor);

const uretilen = new bagli("yapici");
console.log(uretilen.sensor);
console.log(hedef.sensor);
console.log(Object.getPrototypeOf(uretilen) === sensoruYaz.prototype);

const nesne = {
  sensor: "ortulu",
  oku() {
    return this.sensor;
  },
};
console.log(nesne.oku());
console.log(nesne.oku.call(digerHedef));
```

```
acik-bagli
ortulu-denemesi
diger
yapici
ortulu-denemesi
true
ortulu
diger
```

İkinci ve üçüncü satırlar açık bağlanmanın örtülü bağlanmayı yendiğini söyler: bağlı
fonksiyon bir nesnenin özelliği olarak çağrılsa bile yazma işlemi `hedef` üzerinde
gerçekleşir, `digerHedef` değişmez. Dördüncü ve beşinci satırlar yapıcı bağlanmanın açık
bağlanmayı yendiğini gösterir: `new` ile çağrıldığında bağlı hedef göz ardı edilir, yeni
nesne kullanılır ve zincir yapıcının `prototype` nesnesine kurulur.

Sıra tek cümleyle özetlenebilir: **`new` > `bind`/`call`/`apply` > nokta işleci > hiçbiri.**
Beşinci bir durum daha vardır — hiçbir kurala uymayan, kendi `this` değeri olmayan
fonksiyonlar — ve iki ders sonra ele alınacaktır.

#### Özet

- `this` değeri fonksiyonun tanımlandığı yere değil, çağrıldığı biçime bakılarak belirlenir.
- Genel bağlanmada `this`, katı kipte `undefined`, katı olmayan kipte genel nesnedir.
- Örtülü bağlanmada nokta işlecinin solundaki nesne bağlanır; yalnızca son halka sayılır.
- Yöntem bir değişkene atandığında ya da geri çağrı olarak aktarıldığında bağı kopar;
  sınıf yöntemleri bu davranışta ayrıcalıklı değildir.
- Prototip zinciri fonksiyonun nerede bulunacağını, `this` hangi veri üzerinde
  çalışacağını belirler; ikisi bağımsızdır.
- Öncelik sırası: yapıcı bağlanma, açık bağlanma, örtülü bağlanma, genel bağlanma.

#### Sonraki Adım

Bu derste açık bağlanma yalnızca bir kural olarak tanıtıldı; onu kuran üç işlemin
farkları ele alınmadı. `call` ile `apply` argümanları nasıl aktarır, `bind` neden kalıcı
bir bağ üretir ve bu işlemler bir nesnenin zincirinde olmayan bir yöntemi ona nasıl
ödünç verir? Sonraki ders bu üç işlemi ve fonksiyon ödünç alma kalıbını inceleyecek.

### call, apply ve bind

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/call-apply-bind

Bağlamı çağrı yerinde belirleyen üç işlem, argüman aktarma biçimleri, fonksiyon ödünç alma kalıbı, kısmi uygulama ve kalıcı bağın kimlik maliyeti.

Önceki ders açık bağlanmayı bir kural olarak tanıttı ve şunu gösterdi: prototip zinciri
fonksiyonun nerede bulunacağını, `this` hangi veri üzerinde çalışacağını belirler. İkisi
bağımsız olduğu için, bir nesne hiç tanımadığı bir yöntemle çalıştırılabilir.

Bu bağımsızlığı kullanan üç işlem vardır. `call` ve `apply` bağlamı yalnızca o çağrı için
belirler ve fonksiyonu hemen çalıştırır; `bind` ise bağı kalıcı biçimde taşıyan **yeni bir
fonksiyon** üretir. Ders üçünün farklarını, ödünç alma kalıbını ve kalıcı bağın bedelini
ele alır.

#### Argümanların Aktarılması

`call` ile `apply` arasındaki tek fark argümanların nasıl verildiğidir: `call` onları tek
tek alır, `apply` bir dizi olarak.

```js
const olcumDavranisi = {
  ozetle(onEk, ayrac) {
    return `${onEk}${ayrac}${this.sensor}${ayrac}${this.deger}`;
  },
};

const kayit = { sensor: "S-01", deger: 21.4 };
const argumanlar = ["olcum", " | "];

console.log(olcumDavranisi.ozetle.call(kayit, "olcum", " | "));
console.log(olcumDavranisi.ozetle.apply(kayit, argumanlar));
console.log(olcumDavranisi.ozetle.call(kayit, ...argumanlar));

const degerler = [21.4, 19.8, 25.1, 18.2];
console.log(Math.max.apply(null, degerler));
console.log(Math.max(...degerler));

try {
  olcumDavranisi.ozetle.call(null, "olcum", "-");
} catch (hata) {
  console.log(`null baglam: ${hata.constructor.name}`);
}
```

```
olcum | S-01 | 21.4
olcum | S-01 | 21.4
olcum | S-01 | 21.4
25.1
25.1
null baglam: TypeError
```

Yayma işleci dile girdikten sonra `apply` işleminin argüman açma işlevi büyük ölçüde
gereksizleşmiştir; üçüncü ve beşinci satırlar aynı işi yayma ile yapar. `apply`, argüman
listesi çalışma zamanında hesaplanan ve dizi olarak elde tutulan durumlarda hâlâ
doğrudan bir yol sunar.

Son satır bir ayrıntıyı sabitler: katı kipte `null` ya da `undefined` bağlamı genel nesneye
çevrilmez, olduğu gibi aktarılır. Bu yüzden `Math.max.apply(null, ...)` çalışır — `Math.max`
`this` değerini hiç kullanmaz — ama `this` okuyan bir fonksiyon aynı çağrıda hata verir.

#### Fonksiyon Ödünç Alma

Açık bağlanmanın asıl kullanımı **fonksiyon ödünç alma (function borrowing)**: bir
nesnenin zincirinde bulunmayan bir yöntemi, o nesne üzerinde çalıştırmak.

```js
const diziBenzeri = { 0: "S-01", 1: "S-02", 2: "S-03", length: 3 };

console.log(Array.prototype.join.call(diziBenzeri, ";"));
console.log(Array.prototype.map.call(diziBenzeri, (a) => a.toLowerCase()).join(","));
console.log(Array.from(diziBenzeri).join(","));
console.log(Array.isArray(diziBenzeri));

const sozluk = Object.create(null);
sozluk.sensor = "S-01";

console.log(typeof sozluk.hasOwnProperty);
console.log(Object.prototype.hasOwnProperty.call(sozluk, "sensor"));
console.log(Object.hasOwn(sozluk, "sensor"));

console.log(Object.prototype.toString.call([1, 2]));
console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call(new Date(0)));
console.log(Object.prototype.toString.call({ sensor: "S-01" }));
```

```
S-01;S-02;S-03
s-01,s-02,s-03
S-01,S-02,S-03
false
undefined
true
true
[object Array]
[object Null]
[object Date]
[object Object]
```

İlk üç satır **dizi benzeri (array-like)** nesneleri gösterir: sayısal anahtarlar ve bir
`length` özelliği taşıyan, ama zincirinde `Array.prototype` bulunmayan nesneler. Dizi
yöntemleri yalnızca bu iki şeye baktığı için ödünç alınabilirler. Dördüncü satır nesnenin
gerçekten dizi olmadığını doğrular. `Array.from` aynı işi daha okunur biçimde yapar ve
ödünç almaya çoğu durumda tercih edilir.

Beşinci ve altıncı satırlar ilk dersteki prototipsiz sözlüğe bağlanır. Böyle bir nesnede
`hasOwnProperty` yoktur, çünkü zinciri yoktur; sorgu ancak `Object.prototype` üzerinden
ödünç alınarak yapılabilir. `Object.hasOwn` bu ödünç almanın dile eklenmiş biçimidir ve
aynı sonucu daha az dolayımla verir.

Son dört satır, `Object.prototype.toString` yönteminin her değer türü için ayırt edici bir
etiket üretmesinden yararlanır. `typeof` işlecinin ayıramadığı durumları — dizi ile nesne,
`null` ile nesne — bu ödünç alma ayırır.

#### Kalıcı Bağ ve Kısmi Uygulama

`bind`, fonksiyonu çalıştırmaz; bağlamı ve isteğe bağlı olarak baştaki argümanları
sabitlenmiş **yeni bir fonksiyon** döndürür. Argümanların önden sabitlenmesine **kısmi
uygulama (partial application)** denir.

```js
function kalibreEt(kayma, carpan, hamDeger) {
  return (hamDeger + kayma) * carpan;
}

const sensorS01 = kalibreEt.bind(null, -0.4, 1.02);

console.log(kalibreEt(-0.4, 1.02, 21.4).toFixed(3));
console.log(sensorS01(21.4).toFixed(3));
console.log(sensorS01(19.8).toFixed(3));

console.log(kalibreEt.length);
console.log(sensorS01.length);
console.log(sensorS01.name);

const kayit = {
  sensor: "S-01",
  deger: 21.4,
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  },
};

const bagliBicimle = kayit.bicimle.bind(kayit);
const yeniden = bagliBicimle.bind({ sensor: "S-99", deger: 0 });

console.log(bagliBicimle());
console.log(yeniden());
```

```
21.420
21.420
19.788
3
1
bound kalibreEt
S-01: 21.4
S-01: 21.4
```

Kısmi uygulama, her sensör için ayrı bir kalibrasyon fonksiyonu üretmenin doğrudan yoludur;
sabit parametreler bir kez verilir, değişen parametre her çağrıda. Fonksiyonun `length`
özelliği sabitlenen argüman sayısı kadar azalır, `name` özelliği ise türetilmiş olduğunu
belirtir.

Son iki satır kritik bir kuralı gösterir: **bağlı fonksiyon yeniden bağlanamaz.** İkinci
`bind` çağrısı yeni bir fonksiyon üretir ama içteki bağ değişmez, bu yüzden sonuç
değişmemiştir. Aynı kural önceki dersteki öncelik sırasının bir parçasıydı; `new` bu
kuralın tek istisnasıdır.

Kayan noktalı çıktıların `toFixed` ile biçimlendirilmesi bilinçlidir: `(21.4 - 0.4) * 1.02`
ifadesinin doğrudan yazdırılması, ikilik kesir gösteriminden gelen basamakları da
gösterirdi. Bilgisayarlar Nasıl Çalışır kursundaki Kayan Noktalı Sayılar dersi bu davranışı
açıklar.

#### Geri Çağrılarda Bağı Korumak

Önceki dersteki kopma sorununun en sık görüldüğü yer, yöntemin bir geri çağrı olarak
aktarılmasıdır. Üç çözüm vardır ve üçü de aynı sonucu verir.

```js
class OlcumToplayici {
  constructor(sensor) {
    this.sensor = sensor;
    this.degerler = [];
  }
  ekle(deger) {
    this.degerler.push(deger);
  }
  ozet() {
    return `${this.sensor}: ${this.degerler.join(",")}`;
  }
}

const toplayici = new OlcumToplayici("S-01");
const gelenler = [21.4, 19.8, 25.1];

try {
  gelenler.forEach(toplayici.ekle);
} catch (hata) {
  console.log(`baglamsiz: ${hata.constructor.name}`);
}

gelenler.forEach(toplayici.ekle.bind(toplayici));
console.log(toplayici.ozet());

const ikinci = new OlcumToplayici("S-02");
gelenler.forEach(ikinci.ekle, ikinci);
console.log(ikinci.ozet());

const ucuncu = new OlcumToplayici("S-03");
gelenler.forEach((deger) => ucuncu.ekle(deger));
console.log(ucuncu.ozet());
```

```
baglamsiz: TypeError
S-01: 21.4,19.8,25.1
S-02: 21.4,19.8,25.1
S-03: 21.4,19.8,25.1
```

Birinci çözüm `bind` ile kalıcı bağ üretir. İkincisi, `forEach` gibi bazı dizi
yöntemlerinin ikinci parametre olarak aldığı bağlam argümanını kullanır — bu seçenek
`map`, `filter`, `some` ve `every` yöntemlerinde de bulunur, ama her yüksek mertebeden
fonksiyonda bulunmaz. Üçüncüsü çağrıyı bir ok fonksiyonuyla sarar ve bağlanmayı hiç
gerektirmez; nedeni sonraki dersin konusudur.

#### Bağın Kimlik Maliyeti

`bind` her çağrıldığında yeni bir fonksiyon nesnesi üretir. Bu, kimlik karşılaştırmasına
dayanan her yerde sonuç doğurur.

```js
const kayit = {
  sensor: "S-01",
  bicimle() {
    return `kayit: ${this.sensor}`;
  },
};

const birinci = kayit.bicimle.bind(kayit);
const ikinci = kayit.bicimle.bind(kayit);

console.log(birinci() === ikinci());
console.log(birinci === ikinci);
console.log(birinci === kayit.bicimle);

class Dinleyici {
  constructor(ad) {
    this.ad = ad;
    this.isle = this.isle.bind(this);
  }
  isle(olay) {
    return `${this.ad} <- ${olay}`;
  }
}

const a = new Dinleyici("A");
const b = new Dinleyici("B");

console.log(a.isle("olcum"));
console.log(Object.hasOwn(a, "isle"));
console.log(a.isle === b.isle);
console.log(Dinleyici.prototype.isle === Object.getPrototypeOf(a).isle);
console.log(a.isle === Dinleyici.prototype.isle);
```

```
true
false
false
A <- olcum
true
false
true
false
```

İki `bind` çağrısı aynı sonucu üreten ama birbirine eşit olmayan iki fonksiyon verir. Bir
olay dinleyicisini kaydettikten sonra kaldırmak için aynı fonksiyon başvurusu gerektiğinden,
`bind` sonucunun saklanması zorunludur; her seferinde yeniden bağlanan bir fonksiyon
kaldırılamaz.

İkinci bölüm, sınıf yapıcısında yapılan bağlamanın prototip modeline etkisini gösterir.
`this.isle = this.isle.bind(this)` satırı, prototipteki yöntemi gölgeleyen bir **kendi
özellik** yaratır. Sonuç: her örnek kendi bağlı fonksiyonunu taşır, örnekler arasındaki
paylaşım kaybolur, bellekteki fonksiyon sayısı örnek sayısıyla artar. Prototipteki asıl
tanım yerinde durur ama artık kullanılmaz.

Bu, kursun ilk dersindeki ödünleşimin geri dönüşüdür: paylaşılan davranış zincirde
durduğunda ucuzdur, örneğe kopyalandığında pahalı ama bağlamı garantilidir. Kapanışlar
dersi bu ödünleşimi tekrar ve daha genel biçimde ele alacaktır.

#### Özet

- `call` ve `apply` bağlamı yalnızca o çağrı için belirler; fark yalnızca argümanların tek
  tek mi dizi olarak mı verildiğidir.
- Katı kipte `null` ve `undefined` bağlamı genel nesneye çevrilmez.
- Fonksiyon ödünç alma, bir yöntemi zincirinde bulunmadığı bir nesne üzerinde çalıştırır;
  dizi benzeri nesneler ve prototipsiz sözlükler bunun tipik durumlarıdır.
- `bind` yeni bir fonksiyon üretir, argümanları kısmen sabitleyebilir ve ürettiği bağ
  yeniden bağlamayla değiştirilemez.
- Her `bind` çağrısı yeni bir kimlik üretir; kaldırılabilir dinleyicilerde sonucun
  saklanması gerekir.
- Yapıcıda yapılan bağlama, prototipteki yöntemi örnek başına bir kopyayla gölgeler ve
  paylaşımı ortadan kaldırır.

#### Sonraki Adım

Önceki bölümde geri çağrı sorununun üçüncü çözümü bir ok fonksiyonuydu ve hiçbir bağlama
işlemi gerektirmedi. Bunun nedeni, ok fonksiyonlarının `this` değerini çağrı yerinden
almamasıdır — kendilerine ait bir bağlamları yoktur. Sonraki ders bu davranışı, sağladığı
kolaylığı ve ok fonksiyonunun yöntem olarak kullanılamadığı durumları inceleyecek.

### Ok Fonksiyonlarında Bağlam

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/ok-fonksiyonlarinda-baglam

Ok fonksiyonlarının kendi bağlamının olmaması, sözcüksel çözümleme, yapıcı ve prototip eksikliği, sınıf alanı olarak tanımlanan okların bedeli ve yanlış kullanım durumları.

Önceki dersin son örneğinde geri çağrı sorununun üçüncü çözümü bir ok fonksiyonuydu ve
hiçbir bağlama işlemi gerektirmedi. Bunun nedeni, ok fonksiyonunun `this` değerini çağrı
yerinden **almamasıdır**.

Ok fonksiyonlarının kendilerine ait bir `this` bağlaması yoktur. Gövdelerinde geçen `this`,
sıradan bir değişken gibi çözümlenir: içeriden dışarıya doğru, tanımlandıkları kapsam
zincirinde aranır. Programlama Temelleri kursundaki Kapsam ve Yaşam Süresi dersinde
anlatılan sözcüksel kapsam kuralı burada `this` için de geçerlidir. Bu tek fark, önceki
dersteki dört kuralın hiçbirinin ok fonksiyonlarına uygulanmamasına yol açar.

#### Kendi Bağlamı Olmayan Fonksiyon

Ayrımı görmenin en kısa yolu, aynı nesnede iki tanım biçimini yan yana koymaktır.

```js
const kayitYontemli = {
  sensor: "S-01",
  deger: 21.4,
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  },
};

const kayitOkla = {
  sensor: "S-02",
  deger: 19.8,
  bicimle: () => {
    return this === undefined ? "this: undefined" : "this: baska";
  },
};

console.log(kayitYontemli.bicimle());
console.log(kayitOkla.bicimle());

const kopukOk = kayitOkla.bicimle;
console.log(kopukOk());
console.log(kayitOkla.bicimle.call(kayitYontemli));
```

```
S-01: 21.4
this: undefined
this: undefined
this: undefined
```

Nesne değişmezi bir kapsam kurmaz. Ok fonksiyonu modül gövdesinde tanımlandığı için
`this` değeri oradan gelir ve modül gövdesinde bu değer `undefined` olur. Nesnenin
üzerinden çağrılması bunu değiştirmez.

Son satır kuralı kesinleştirir: `call` bile ok fonksiyonunun bağlamını değiştiremez.
Bağlanacak bir yer olmadığı için açık bağlanma sessizce etkisiz kalır — hata da vermez.
`bind` de aynı biçimde etkisizdir; yeni bir fonksiyon üretir ama içteki `this`
çözümlemesine dokunmaz.

#### Sözcüksel Çözümlemenin Yararı

Kendi bağlamının olmaması bir eksiklik gibi görünür; asıl değeri, bir yöntem içinden
verilen geri çağrılarda ortaya çıkar. Yöntemin `this` değeri, içeride tanımlanan ok
fonksiyonuna kendiliğinden taşınır.

```js
class OlcumSerisi {
  constructor(sensor, degerler) {
    this.sensor = sensor;
    this.degerler = degerler;
  }

  etiketliOkla() {
    return this.degerler.map((deger) => `${this.sensor}:${deger}`).join(" ");
  }

  etiketliIsleve() {
    return this.degerler
      .map(function (deger) {
        return `${this.sensor}:${deger}`;
      })
      .join(" ");
  }

  etiketliYedekle() {
    const kendisi = this;
    return this.degerler
      .map(function (deger) {
        return `${kendisi.sensor}:${deger}`;
      })
      .join(" ");
  }
}

const seri = new OlcumSerisi("S-01", [21.4, 19.8]);

console.log(seri.etiketliOkla());
try {
  console.log(seri.etiketliIsleve());
} catch (hata) {
  console.log(`islev ifadesi: ${hata.constructor.name}`);
}
console.log(seri.etiketliYedekle());
```

```
S-01:21.4 S-01:19.8
islev ifadesi: TypeError
S-01:21.4 S-01:19.8
```

İkinci yöntemdeki işlev ifadesi kendi `this` bağlamasını kurar ve geri çağrı olarak
çağrıldığı için bu bağlama genel kurala düşer; katı kipte `undefined` olur ve okuma hata
yükseltir. Üçüncü yöntem, bağlamı sıradan bir değişkene kopyalar; ok fonksiyonları dile
girmeden önce yaygın olan çözüm budur ve hâlâ doğru çalışır. Birinci yöntem aynı işi ek
bir ad kullanmadan yapar.

Kural şu biçimde özetlenebilir: **çağıran bir bağlam sağlamıyorsa ok fonksiyonu doğru
seçimdir.** Geri çağrılar, dizi yöntemlerine verilen işlevler ve bir yöntemin içinde
tanımlanan yardımcı fonksiyonlar bu tanıma girer.

#### Eksik Olan Diğer Şeyler

Ok fonksiyonlarında olmayan tek şey `this` değil. `arguments` nesnesi, `new.target` ve
`prototype` özelliği de bulunmaz. Sonuncusu, ok fonksiyonlarının yapıcı olarak
kullanılamamasının doğrudan nedenidir.

```js
const okKalibre = (deger) => deger * 1.02;
function islevKalibre(deger) {
  return deger * 1.02;
}

console.log("prototype" in okKalibre);
console.log("prototype" in islevKalibre);
console.log(typeof okKalibre);

try {
  new okKalibre(1);
} catch (hata) {
  console.log(`ok ile new: ${hata.constructor.name}`);
}

function disIslev() {
  const icOk = () => arguments[0];
  return icOk();
}
console.log(disIslev("dis-arguman"));

const kalanIle = (...argumanlar) => argumanlar.length;
console.log(kalanIle(1, 2, 3));

console.log(okKalibre.name);
console.log(okKalibre.length);
```

```
false
true
function
ok ile new: TypeError
dis-arguman
3
okKalibre
1
```

İkinci derste `new` işlecinin ilk adımı "prototipi fonksiyonun `prototype` nesnesine bağlı
boş bir nesne yaratmak" olarak yazılmıştı. Ok fonksiyonunda böyle bir nesne bulunmadığı
için o adım hiç uygulanamaz; hata bu eksikliğin sonucudur.

`arguments` de sözcüksel olarak çözümlenir: ok fonksiyonunun içinde kullanıldığında
kendisini saran işlevin argümanlarını verir. Bu davranışa güvenmek yerine kalan
parametreler kullanılmalıdır; hem ok fonksiyonlarında hem de işlevlerde aynı biçimde
çalışır ve gerçek bir dizi üretir. Son iki satır ok fonksiyonlarının ad ve parametre sayısı
gibi sıradan fonksiyon özelliklerini taşımaya devam ettiğini gösterir.

#### Sınıf Alanı Olarak Ok Fonksiyonu

Sınıf gövdesinde bir alanın ok fonksiyonuna atanması, bağlanma sorununu kesin biçimde
çözer. Bedeli prototip modelinde ödenir.

```js
class OlcumToplayici {
  constructor(sensor) {
    this.sensor = sensor;
    this.degerler = [];
  }

  ekle(deger) {
    this.degerler.push(deger);
    return this.degerler.length;
  }

  eklePratik = (deger) => {
    this.degerler.push(deger);
    return this.degerler.length;
  };
}

const a = new OlcumToplayici("S-01");
const b = new OlcumToplayici("S-02");

console.log(Object.getOwnPropertyNames(OlcumToplayici.prototype).join(","));
console.log(Object.getOwnPropertyNames(a).join(","));
console.log(a.ekle === b.ekle);
console.log(a.eklePratik === b.eklePratik);

const kopukPratik = a.eklePratik;
console.log(kopukPratik(21.4));
console.log(a.degerler.join(","));

console.log(a.eklePratik.call(b, 99));
console.log(a.degerler.join(","));
console.log(b.degerler.join(",") || "(bos)");
```

```
constructor,ekle
eklePratik,sensor,degerler
true
false
1
21.4
2
21.4,99
(bos)
```

İlk iki satır ayrımı gösterir: `ekle` prototipte durur; `eklePratik` her örneğin kendi
özelliğidir. Üçüncü ve dördüncü satırlar sonucu ölçer — prototipteki yöntem tüm örneklerde
aynı fonksiyondur; ok alanı her örnekte ayrıdır.

Beşinci ve altıncı satırlar kazancı gösterir: nesnesinden koparılmış ok alanı doğru
toplayıcıya yazmaya devam eder. Son üç satır ise aynı özelliğin diğer yüzüdür — `call` ile
başka bir toplayıcıya yönlendirme denemesi etkisizdir; değer yine `a` nesnesine yazılır.

Ödünleşim nettir. Ok alanı bağlamı garanti eder ama örnek başına bir fonksiyon nesnesi
üretir, paylaşımı ortadan kaldırır ve yeniden yönlendirmeyi imkânsız kılar. Prototipteki
yöntem paylaşımlıdır ve yönlendirilebilir ama bağlamı çağrı yerine emanet eder. Önceki
dersteki yapıcıda `bind` kalıbı da aynı ödünleşimin bir başka yazımıdır.

#### Ok Fonksiyonunun Yanlış Olduğu Yerler

Bir kural olarak: **bir fonksiyonun `this` üzerinden nesnesine erişmesi gerekiyorsa ok
fonksiyonu olamaz.** Bu, nesne değişmezindeki yöntemleri ve prototipe yazılan davranışları
kapsar.

```js
const olcumDavranisi = {
  bicimle: () => `${this?.sensor}`,
};

const kayit = Object.create(olcumDavranisi);
kayit.sensor = "S-01";
console.log(kayit.bicimle());

const dogruDavranis = {
  bicimle() {
    return `${this.sensor}`;
  },
};
const dogruKayit = Object.create(dogruDavranis);
dogruKayit.sensor = "S-01";
console.log(dogruKayit.bicimle());

const sayacUreteci = () => {
  let sayi = 0;
  return {
    artir: () => (sayi += 1),
    oku: () => sayi,
  };
};

const sayac = sayacUreteci();
sayac.artir();
sayac.artir();
console.log(sayac.oku());
```

```
undefined
S-01
2
```

İlk kayıt prototipini doğru biçimde almıştır, ama zincirdeki fonksiyon `this` değerini
zincirden değil kendi tanımlandığı kapsamdan okur; sonuç `undefined` olur. Yalnızca tanım
biçimi değiştirilerek doğru davranış elde edilir.

Son bölüm ise ok fonksiyonlarının doğru kullanıldığı ikinci kalıbı önceden gösterir:
nesnenin verisi `this` yerine **kapatılmış bir değişkende** tutulduğunda `this` hiç
gerekmez ve bağlanma sorunu ortadan kalkar. Bu kuruluşun adı kapanıştır ve sonraki dersin
konusudur.

#### Özet

- Ok fonksiyonlarının kendi `this` bağlaması yoktur; gövdedeki `this`, tanımlandıkları
  kapsamda sözcüksel olarak çözümlenir.
- `call`, `apply` ve `bind` ok fonksiyonunun bağlamını değiştiremez; sessizce etkisiz kalır.
- Ok fonksiyonu bir yöntemin içinden verilen geri çağrılarda bağlamı kendiliğinden taşır;
  bağlamı çağıran sağlamıyorsa doğru seçimdir.
- Ok fonksiyonlarında `prototype`, `arguments` ve `new.target` bulunmaz; bu yüzden yapıcı
  olarak kullanılamazlar.
- Sınıf alanı olarak tanımlanan ok fonksiyonu örnek başına bir kopya üretir: bağlamı
  garanti eder, paylaşımı ve yönlendirilebilirliği kaybettirir.
- Nesne değişmezindeki yöntemler ve prototipe yazılan davranışlar ok fonksiyonu olamaz.

#### Sonraki Adım

Son örnekte sayaç, durumunu bir nesne özelliğinde değil, kendisini üreten fonksiyonun
yerel değişkeninde tuttu. Fonksiyon döndükten sonra bile o değişken yaşamaya devam etti ve
yalnızca döndürülen fonksiyonlar tarafından görülebildi. Sonraki ders bu yapıyı —
kapanışları — ele alacak; kapatılan değişkenlerin yaşam süresini inceleyecek ve durum
gizlemenin bu yolunu prototip zinciriyle karşılaştıracak.

### Kapanışlar

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/kapanislar

Kapatılan değişkenlerin yaşam süresi ve paylaşımı, kapanışla kapsülleme, aynı türün üç kuruluşunun karşılaştırması, döngü değişkeni tuzağı ve not alma kalıbı.

Önceki dersin son örneğinde bir sayaç, durumunu nesne özelliğinde değil, kendisini üreten
fonksiyonun yerel değişkeninde tuttu. Fonksiyon döndü, ama değişken yaşamaya devam etti ve
yalnızca döndürülen fonksiyonlar tarafından görülebildi.

**Kapanış (closure)**, bir fonksiyonun tanımlandığı kapsamdaki değişkenlere, o kapsam
sona erdikten sonra da erişebilmesidir. Programlama Temelleri kursundaki Kapsam ve Yaşam
Süresi dersi kavramı tanıtmıştı; bu ders onu nesne modelinin içine yerleştirir. Ortaya
çıkan sonuç kursun ekseni açısından belirleyicidir: kapanış, durumu prototip zincirine hiç
dokunmadan gizleyen ikinci bir kuruluş yolu sunar.

#### Kapanışla Kapsülleme

Ölçüm kaydını bu kez bir fabrika işleviyle üretelim. Ham değer ve kalibrasyon kayması
işlevin parametrelerinde kalır; döndürülen nesneye hiç yazılmaz.

```js
function olcumKaydiOlustur(sensor, hamDeger, kayma) {
  let okumaSayisi = 0;

  function okunanDeger() {
    okumaSayisi += 1;
    return hamDeger + kayma;
  }

  return {
    sensor,
    oku: okunanDeger,
    bicimle() {
      return `${sensor}: ${okunanDeger().toFixed(2)}`;
    },
    denetim() {
      return `okuma=${okumaSayisi}`;
    },
  };
}

const kayit = olcumKaydiOlustur("S-01", 21.4, -0.4);

console.log(kayit.oku().toFixed(2));
console.log(kayit.bicimle());
console.log(kayit.denetim());
console.log(JSON.stringify(kayit));
console.log(Object.keys(kayit).join(","));
console.log(kayit.hamDeger);
console.log(kayit.okumaSayisi);
```

```
21.00
S-01: 21.00
okuma=2
{"sensor":"S-01"}
sensor,oku,bicimle,denetim
undefined
undefined
```

Ham değer, kayma ve okuma sayacı hiçbir listeleme işleminde görünmez; dizileştirmeye
girmez; dışarıdan okunamaz ve yazılamaz. Sınıf sözdiziminin özel alanlarıyla aynı
sonuçtur, ama mekanizması bambaşkadır: gizlilik özellik tanımlayıcılarından değil,
kapsamdan gelir. Bir değişken yalnızca onu gören fonksiyonlar tarafından erişilebilir
olduğu için, kapsam sınırının kendisi kapsüllemeyi sağlar.

Denetim sayacının `2` olması, `bicimle` çağrısının da aynı iç fonksiyonu kullandığını
gösterir: üç fonksiyon aynı kapsamı — dolayısıyla aynı değişkenleri — paylaşır.

#### Aynı Türün Üç Kuruluşu

Kurs boyunca kullanılan ölçüm kaydı artık üç ayrı biçimde kurulabiliyor. Üçünü yan yana
koymak, ödünleşimi tek bir çıktıda görünür kılar.

```js
function duzKayit(sensor, deger) {
  return {
    sensor,
    deger,
    bicimle() {
      return `${this.sensor}: ${this.deger}`;
    },
  };
}

function kapanisliKayit(sensor, deger) {
  return {
    bicimle() {
      return `${sensor}: ${deger}`;
    },
  };
}

class SinifKayit {
  constructor(sensor, deger) {
    this.sensor = sensor;
    this.deger = deger;
  }
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  }
}

const duzA = duzKayit("S-01", 21.4);
const duzB = duzKayit("S-02", 19.8);
const kapA = kapanisliKayit("S-01", 21.4);
const kapB = kapanisliKayit("S-02", 19.8);
const sinA = new SinifKayit("S-01", 21.4);
const sinB = new SinifKayit("S-02", 19.8);

console.log([duzA.bicimle(), kapA.bicimle(), sinA.bicimle()].join(" | "));

console.log(`duz  : paylasim=${duzA.bicimle === duzB.bicimle}, kendi=${Object.keys(duzA).join("+")}, json=${JSON.stringify(duzA)}`);
console.log(`kapan: paylasim=${kapA.bicimle === kapB.bicimle}, kendi=${Object.keys(kapA).join("+")}, json=${JSON.stringify(kapA)}`);
console.log(`sinif: paylasim=${sinA.bicimle === sinB.bicimle}, kendi=${Object.keys(sinA).join("+")}, json=${JSON.stringify(sinA)}`);

console.log(Object.getPrototypeOf(duzA) === Object.prototype);
console.log(Object.getPrototypeOf(kapA) === Object.prototype);
console.log(Object.getPrototypeOf(sinA) === SinifKayit.prototype);

const kopukSinif = sinA.bicimle;
const kopukKapanis = kapA.bicimle;
try {
  kopukSinif();
} catch (hata) {
  console.log(`sinif kopuk: ${hata.constructor.name}`);
}
console.log(`kapanis kopuk: ${kopukKapanis()}`);
```

```
S-01: 21.4 | S-01: 21.4 | S-01: 21.4
duz  : paylasim=false, kendi=sensor+deger+bicimle, json={"sensor":"S-01","deger":21.4}
kapan: paylasim=false, kendi=bicimle, json={}
sinif: paylasim=true, kendi=sensor+deger, json={"sensor":"S-01","deger":21.4}
true
true
true
sinif kopuk: TypeError
kapanis kopuk: S-01: 21.4
```

İlk satır üç kuruluşun **aynı davranışı** ürettiğini doğrular. Kalan satırlar farkları
sıralar:

- **Paylaşım.** Yalnızca sınıf kuruluşunda fonksiyon tektir; diğer ikisinde her nesne
  kendi fonksiyonunu taşır. Bin kayıt için sınıf bir fonksiyon, diğerleri bin fonksiyon
  üretir.
- **Görünürlük.** Kapanışlı kuruluşta veri hiç özellik olmadığı için dizileştirme boş
  nesne verir. Verinin dışarı çıkması gerekiyorsa açık bir yöntem yazılmalıdır.
- **Zincir.** Fabrika işlevleri sıradan nesne değişmezi döndürdüğü için prototipleri
  `Object.prototype` olur; tür sorgusu, ortak davranış ekleme ve alt tür türetme
  yollarının hiçbiri çalışmaz.
- **Bağlanma.** Kapanışlı kuruluşta `this` hiç kullanılmadığı için yöntem nesnesinden
  koparıldığında bile çalışır; sınıf yöntemi aynı durumda hata verir.

Seçim ölçütü buradan çıkar. Çok sayıda nesne, tür sorgusu, alt tür ve ortak davranış
gerekiyorsa sınıf; az sayıda nesne, kesin gizlilik ve bağlamdan bağımsız çağrı gerekiyorsa
kapanış. İkisi birbirinin yerine geçen seçenekler değil, farklı maliyet profilleri olan
iki kuruluştur.

#### Kapatılan Değişkenin Yaşam Süresi

Kapanış bir **değeri** değil, bir **değişkeni** kapatır. Aynı kapsamı paylaşan
fonksiyonlar aynı değişkeni görür; farklı çağrılardan üretilen kapanışlar ise ayrı
kapsamlara sahiptir.

```js
function olcumSayaciOlustur(baslangic) {
  let sayi = baslangic;
  return {
    artir: () => (sayi += 1),
    azalt: () => (sayi -= 1),
    oku: () => sayi,
  };
}

const birinci = olcumSayaciOlustur(0);
const ikinci = olcumSayaciOlustur(100);

birinci.artir();
birinci.artir();
ikinci.azalt();

console.log(birinci.oku());
console.log(ikinci.oku());

function tamponOlustur() {
  const degerler = [];
  return {
    ekle(deger) {
      degerler.push(deger);
      return degerler.length;
    },
    ortalama() {
      if (degerler.length === 0) return 0;
      return degerler.reduce((toplam, d) => toplam + d, 0) / degerler.length;
    },
    boyut: () => degerler.length,
  };
}

const tampon = tamponOlustur();
tampon.ekle(21.4);
tampon.ekle(19.8);
tampon.ekle(25.1);

console.log(tampon.boyut());
console.log(tampon.ortalama().toFixed(3));
console.log(Object.keys(tampon).join(","));
```

```
2
99
3
22.100
ekle,ortalama,boyut
```

İki sayaç birbirinden bağımsızdır; her `olcumSayaciOlustur` çağrısı yeni bir kapsam
üretir. Buna karşılık aynı çağrıdan dönen üç fonksiyon tek bir `sayi` değişkenini
paylaşır — biri değiştirdiğinde diğeri değişikliği görür.

Yaşam süresi kuralı da buradan çıkar: **kapatılan değişken, onu gören son fonksiyon
erişilebilir olduğu sürece yaşar.** `tamponOlustur` işlevi döndükten sonra `degerler`
dizisine hiçbir dış başvuru yoktur, ama üç fonksiyon onu gördüğü için dizi bellekte
kalır. Bu, kapanışların bellek maliyetinin kaynağıdır: büyük bir veri yapısını kapatan
küçük bir fonksiyonun saklanması, o veri yapısının tamamını yaşatır. Bellek yaşam
döngüsü ve erişilebilirlik temelli toplama, Eşzamansız JavaScript ve Çalışma Zamanı
kursunun konusudur.

#### Döngü Değişkeni Tuzağı

Kapanışın değişkeni kapattığı kuralının en bilinen sonucu, döngü içinde üretilen
fonksiyonlarda görülür.

```js
const varIsleri = [];
for (var sayac = 0; sayac < 3; sayac += 1) {
  varIsleri.push(() => `sensor-${sayac}`);
}

const letIsleri = [];
for (let sayac = 0; sayac < 3; sayac += 1) {
  letIsleri.push(() => `sensor-${sayac}`);
}

console.log(varIsleri.map((f) => f()).join(","));
console.log(letIsleri.map((f) => f()).join(","));

const elleIsler = [];
for (var i = 0; i < 3; i += 1) {
  elleIsler.push(
    (function (sabitlenen) {
      return () => `sensor-${sabitlenen}`;
    })(i),
  );
}
console.log(elleIsler.map((f) => f()).join(","));
```

```
sensor-3,sensor-3,sensor-3
sensor-0,sensor-1,sensor-2
sensor-0,sensor-1,sensor-2
```

`var` ile bildirilen sayaç fonksiyon kapsamlıdır: tüm döngü boyunca **tek bir değişken**
vardır ve üç kapanış da onu kapatır. Döngü bittiğinde değişkenin değeri `3` olduğu için üç
fonksiyon da aynı sonucu verir. `let` her yinelemede yeni bir bağlama üretir, bu yüzden
her kapanış kendi değişkenini kapatır.

Üçüncü blok, blok kapsamlı bildirimler dile girmeden önce kullanılan çözümü gösterir: her
yinelemede hemen çağrılan bir işlevle yeni bir kapsam açmak ve sayacı parametre olarak
sabitlemek. Bu kalıp sonraki dersin konusudur.

#### Durum Taşıyan Sarmalayıcı

Kapanışın en verimli kullanımı, bir fonksiyona onu değiştirmeden durum eklemektir.
Programlama Temelleri kursundaki Özyineleme dersinde tanıtılan **not alma (memoization)**
tam olarak budur: hesaplanan sonuçlar kapanıştaki bir sözlükte saklanır.

```js
function notAlarakSar(islev) {
  const onbellek = Object.create(null);
  let gercekCagri = 0;

  function sarilmis(anahtar) {
    if (anahtar in onbellek) return onbellek[anahtar];
    gercekCagri += 1;
    const sonuc = islev(anahtar);
    onbellek[anahtar] = sonuc;
    return sonuc;
  }

  sarilmis.gercekCagriSayisi = () => gercekCagri;
  return sarilmis;
}

const acikTablo = { "S-01": -0.4, "S-02": 0.15, "S-03": 0 };
const guvenliTablo = Object.assign(Object.create(null), acikTablo);

const acikOku = notAlarakSar((sensor) => acikTablo[sensor] ?? 0);
const guvenliOku = notAlarakSar((sensor) => guvenliTablo[sensor] ?? 0);

console.log(acikOku("S-01"));
console.log(acikOku("S-01"));
console.log(acikOku("S-02"));
console.log(acikOku("S-01"));
console.log(acikOku.gercekCagriSayisi());

console.log(typeof acikOku("toString"));
console.log(typeof guvenliOku("toString"));
console.log(guvenliOku("toString"));
console.log(guvenliOku.gercekCagriSayisi());
console.log(typeof acikOku.onbellek);
```

```
-0.4
-0.4
0.15
-0.4
2
function
number
0
1
undefined
```

Dört çağrının yalnızca ikisi asıl işlevi çalıştırır; gerisi önbellekten döner. Sayaç
fonksiyona bir özellik olarak iliştirilmiştir, ama sayacın kendisi hâlâ kapanıştadır — son
satır `onbellek` sözlüğünün dışarıdan görünmediğini doğrular.

Altıncı ve yedinci satırlar ilk dersteki uyarının bedelini gösterir. Sıradan bir nesne
değişmeziyle kurulan kalibrasyon tablosunda `"toString"` anahtarı sorulduğunda zincirden
gelen fonksiyon bulunur ve `??` işleci onu boş saymaz. `Object.create(null)` ile kurulan
tabloda zincir olmadığı için doğru sonuç döner. Anahtarları program dışından gelen her
eşlemede aynı önlem gerekir; dilin bu iş için ayrılmış türü olan `Map`, İleri
Koleksiyonlar konusunda ele alınacaktır.

#### Özet

- Kapanış, bir fonksiyonun tanımlandığı kapsamdaki değişkenlere o kapsam sona erdikten
  sonra da erişebilmesidir.
- Kapanışla kurulan gizlilik özellik tanımlayıcılarından değil kapsam sınırından gelir;
  veri hiç özellik olmadığı için listelenmez ve dizileştirilmez.
- Kapanışlı kuruluş bağlamdan bağımsızdır ama davranışı paylaşmaz ve prototip zinciri
  kurmaz; sınıf kuruluşu bunun tersidir.
- Kapanış değeri değil değişkeni kapatır; aynı kapsamdan üretilen fonksiyonlar aynı
  değişkeni paylaşır, farklı çağrılar bağımsız kapsamlar üretir.
- Kapatılan değişken, onu gören son fonksiyon erişilebilir olduğu sürece yaşar; bellek
  maliyeti buradan doğar.
- `var` ile bildirilen döngü sayacı tek bir değişkendir; `let` her yinelemede yeni bağlama
  üretir.

#### Sonraki Adım

Döngü tuzağının eski çözümünde bir fonksiyon tanımlanır tanımlanmaz çağrılıyor ve yalnızca
yeni bir kapsam açmak için kullanılıyordu. Bu kalıbın adı vardır ve blok kapsamlı
bildirimlerden önce kütüphane sınırlarını çizmenin de tek yoluydu. Sonraki ders hemen
çağrılan fonksiyonları, kapsam yalıtımı işlevini ve bu işlevin dilin sonraki
yeteneklerince ne ölçüde devralındığını ele alacak.

### Hemen Çağrılan Fonksiyonlar

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/hemen-cagrilan-fonksiyonlar

Fonksiyon bildirimini ifadeye çeviren yazım biçimleri, kapsam yalıtımı ve açığa çıkaran modül kalıbı, tek seferlik kurulum ve blok kapsamıyla karşılaştırma.

Önceki dersteki döngü tuzağının eski çözümünde bir fonksiyon tanımlanır tanımlanmaz
çağrılıyordu. Amacı bir değer üretmek değil, yeni bir kapsam açmaktı. Bu kalıbın adı
**hemen çağrılan fonksiyon (immediately invoked function expression)**.

Kalıp iki iş yapar: gövdesindeki bildirimleri dış kapsamdan yalıtır ve içeriden yalnızca
seçilen değerleri dışarı verir. Kapanışlar dersindeki fabrika işlevlerinin, tek bir nesne
için bir kez çalıştırılan biçimi olarak da okunabilir.

#### Fonksiyonu İfadeye Çevirmek

Bir deyimin başında `function` sözcüğü görüldüğünde ayrıştırıcı bunu bir **bildirim**
sayar ve bildirimler hemen çağrılamaz. Kalıbın sarmalayıcı parantezleri bu yüzden vardır:
fonksiyonu deyim konumundan çıkarıp ifade konumuna taşırlar.

```js
try {
  new Function("function () { return 1; }();");
} catch (hata) {
  console.log(`bildirim olarak: ${hata.constructor.name}`);
}

const birinci = (function () {
  return "sarmalanan cagri";
})();

const ikinci = (function () {
  return "icerden kapanan";
}());

const ucuncu = (() => "ok bicimi")();

const dorduncu = void (function () {
  console.log("islec ile ifadeye cevrildi");
})();

console.log(birinci);
console.log(ikinci);
console.log(ucuncu);
console.log(dorduncu);
```

```
bildirim olarak: SyntaxError
islec ile ifadeye cevrildi
sarmalanan cagri
icerden kapanan
ok bicimi
undefined
```

İlk deneme, bildirim biçiminin ayrıştırılamadığını gösterir; `new Function` çağrısı
metni ayrıştırdığı için hata çalışma zamanında yakalanabilir. Kalan üç biçim geçerlidir
ve aralarında davranış farkı yoktur: çağrı parantezleri sarmalayıcının içinde ya da
dışında olabilir ve ok fonksiyonu da aynı işi görür.

Dördüncü biçim, bir işleçle ifade konumuna zorlamayı gösterir. `void` işleci sonucu
`undefined` yaptığı için dönüş değeri kullanılmayan durumlarda tercih edilirdi. Çıktı
sırası da bir ayrıntıyı belli eder: gövdedeki yazdırma, `console.log(birinci)`
satırından önce çalışır, çünkü fonksiyon tanımlandığı yerde hemen çağrılır.

#### Kapsam Yalıtımı ve Modül Kalıbı

Kalıbın asıl kullanımı, bir grup ilişkili işlevi ortak ve gizli bir durum çevresinde
toplamaktır. İçeride tanımlananlar dışarıdan görünmez; yalnızca döndürülen nesnedeki adlar
erişilebilir olur. Bu kuruluşa **açığa çıkaran modül kalıbı (revealing module pattern)**
denir.

```js
const olcumDefteri = (function () {
  const kayitlar = [];
  let sonrakiSira = 1;

  function ekle(sensor, deger) {
    const kayit = { sira: sonrakiSira, sensor, deger };
    sonrakiSira += 1;
    kayitlar.push(kayit);
    return kayit.sira;
  }

  function sensoreGore(sensor) {
    return kayitlar.filter((k) => k.sensor === sensor).length;
  }

  function ozet() {
    return `${kayitlar.length} kayit, sonraki sira ${sonrakiSira}`;
  }

  return { ekle, sensoreGore, ozet };
})();

console.log(olcumDefteri.ekle("S-01", 21.4));
console.log(olcumDefteri.ekle("S-02", 19.8));
console.log(olcumDefteri.ekle("S-01", 22.0));
console.log(olcumDefteri.sensoreGore("S-01"));
console.log(olcumDefteri.ozet());
console.log(Object.keys(olcumDefteri).join(","));
console.log(typeof olcumDefteri.kayitlar);
console.log(JSON.stringify(olcumDefteri));
```

```
1
2
3
2
3 kayit, sonraki sira 4
ekle,sensoreGore,ozet
undefined
{}
```

Kayıt dizisi ve sıra sayacı dışarıdan erişilemez; defterin arayüzü yalnızca üç işlevden
oluşur. Sıra numarasının dışarıdan bozulamaması, Programlama Temelleri kursundaki Nesneye
Dayalı Programlamaya Giriş dersinde tanıtılan sınıf değişmezinin korunmasıdır — burada
kapsülleme sınıf yerine kapsam sınırıyla sağlanır.

Kalıbın sınıftan ayrıldığı nokta sayıdır: bu kuruluş **tek bir nesne** üretir. Aynı türden
ikinci bir defter isteniyorsa hemen çağırmadan, sıradan bir fabrika işlevi olarak
kullanılmalıdır. Tek örnekli yapılar — bir uygulama yapılandırması, bir kütük yazıcısı,
bir sayaç — bu kalıbın doğal alanıdır.

#### Tek Seferlik Kurulum

İkinci yaygın kullanım, bir değeri kurmak için gereken ara adımların dışarı sızmasını
önlemektir. Değer bir kez hesaplanır; ara değişkenler kalıbın içinde kalır.

```js
const KALIBRASYON = (function () {
  const hamSatirlar = ["S-01;-0.4", "S-02;0.15", "S-03;0"];
  const tablo = Object.create(null);
  for (const satir of hamSatirlar) {
    const [sensor, kayma] = satir.split(";");
    tablo[sensor] = Number(kayma);
  }
  return Object.freeze(tablo);
})();

console.log(KALIBRASYON["S-01"]);
console.log(KALIBRASYON["S-02"]);
console.log(Object.isFrozen(KALIBRASYON));
console.log(Object.getPrototypeOf(KALIBRASYON));
console.log(typeof hamSatirlar);

try {
  KALIBRASYON["S-04"] = 1;
} catch (hata) {
  console.log(`ekleme: ${hata.constructor.name}`);
}
console.log(Object.keys(KALIBRASYON).join(","));
```

```
-0.4
0.15
true
null
undefined
ekleme: TypeError
S-01,S-02,S-03
```

Ham satırlar ve döngü değişkeni dış kapsamda hiç görünmez; geriye yalnızca kullanıma hazır
tablo kalır. Tablo `Object.create(null)` ile kurulduğu için zinciri yoktur ve
`Object.freeze` ile dondurulduğu için sonradan değiştirilemez — Nesne Prototipi ile
Değişmezlik Teknikleri derslerindeki iki karar burada birlikte uygulanmıştır.

Bu kalıp, kurulum maliyeti olan sabitler için uygundur. Kurulumun her kullanımda değil bir
kez yapılması, Algoritmalar kursundaki önişleme fikrinin küçük ölçekli bir örneğidir.

#### Bağımlılığı Dışarıdan Vermek

Kalıbın ikinci yaygın kullanımı, sarmalayıcının **parametre listesidir**. Dışarıdan
kullanılan her ad açıkça geçirilirse, gövde içinde neye bağımlı olunduğu okunur hâle
gelir ve ad araması dış kapsamlara çıkmaz.

```js
const ayarlar = { esik: 25 };

const rapor = (function (kaynak, birim) {
  const olcumler = [21.4, 26.8, 23.1];
  const asanlar = olcumler.filter((d) => d > kaynak.esik);
  return `${asanlar.length} olcum ${kaynak.esik}${birim} esigini asti`;
})(ayarlar, "C");

console.log(rapor);

(function kurulumDenetimi() {
  try {
    null.deger;
  } catch (hata) {
    const ilkSatir = hata.stack.split("\n")[1].trim().split(" ")[1];
    console.log("yığıt izindeki ad:", ilkSatir);
  }
})();
```

```
1 olcum 25C esigini asti
yığıt izindeki ad: kurulumDenetimi
```

İkinci çağrı bir ayrıntıyı gösterir: sarmalayıcıya ad verilebilir. Ad dışarıdan
erişilebilir değildir — yalnızca fonksiyonun kendi gövdesinde ve **yığıt izinde**
görünür. Adsız bir sarmalayıcıda aynı satır anonim olarak listelenir; hata ayıklarken
kalıbın adlandırılmış biçimi tercih edilir.

Yığıt izinin biçimi çalışma zamanına göre değişir; burada yalnızca adın listede yer
alması esastır.

#### Blok Kapsamı ve Modüllerle Karşılaştırma

Kalıbın iki işlevi de dilin sonraki yetenekleriyle büyük ölçüde karşılanır. Blok kapsamlı
bildirimler yalıtımı sağlar; modül sistemi dosya düzeyinde ad alanı verir.

```js
let blokSonucu;
{
  const gecici = [21.4, 19.8, 25.1];
  blokSonucu = gecici.length;
}
console.log(blokSonucu);
console.log(typeof gecici);

const ifadeSonucu = (() => {
  const gecici = [21.4, 19.8, 25.1];
  return gecici.reduce((toplam, d) => toplam + d, 0) / gecici.length;
})();
console.log(ifadeSonucu.toFixed(3));

var kapsamDenemesi = "disarida";
(function () {
  var kapsamDenemesi = "iceride";
  console.log(kapsamDenemesi);
})();
console.log(kapsamDenemesi);
```

```
3
undefined
22.100
iceride
disarida
```

Blok, `const` ve `let` bildirimlerini yalıtır ve amaç yalnızca yalıtımsa hemen çağrılan
fonksiyondan daha yalın bir yoldur. Farkı ikinci bölüm gösterir: blok bir deyimdir, değer
üretmez; sonucu dışarı taşımak için önceden bildirilmiş bir değişkene atama gerekir.
Hemen çağrılan fonksiyon bir ifadedir ve doğrudan bir sabite bağlanabilir.

Üçüncü bölüm, kalıbın hâlâ gerekli olduğu tek durumu gösterir: `var` ile bildirilen adlar
blok kapsamına girmez, yalnızca fonksiyon kapsamıyla yalıtılır.

Modül sistemi ise ad alanı işlevini devralır. Her dosya kendi kapsamıdır; dışa
aktarılmayan hiçbir ad başka dosyadan görülmez. Dosya düzeyinde ad çakışmasını önlemek
için kalıba başvurmak gereksizdir. Modül sistemlerinin ayrıntısı Modüller, Araçlar ve
Ekosistem kursunun konusudur.

Geriye kalan kullanım alanı üç başlıkta toplanabilir: tek örnekli bir arayüzü gizli
durumla birlikte kurmak, bir sabitin kurulum adımlarını yalıtmak ve blok kapsamının
yetmediği `var` bildirimlerini sınırlamak.

#### Özet

- Deyim başındaki `function` sözcüğü bildirim olarak ayrıştırıldığı için, hemen çağrılan
  fonksiyon bir sarmalayıcıyla ifade konumuna taşınır.
- Kalıp yeni bir kapsam açar; içeride tanımlananlar dışarıdan görünmez, yalnızca
  döndürülen değer erişilebilir olur.
- Açığa çıkaran modül kalıbı, gizli durumu olan tek bir nesne üretir; birden çok örnek
  gerekiyorsa fabrika işlevi kullanılır.
- Tek seferlik kurulumda ara değişkenler yalıtılır ve sonuç dondurulmuş bir sabit olarak
  dışarı verilir.
- Blok kapsamı yalıtım işlevini karşılar ama değer üretmez; modül sistemi ad alanı
  işlevini devralır.

#### Sonraki Adım

Bu konu boyunca fonksiyonlar sürekli değer gibi kullanıldı: geri çağrı olarak aktarıldı,
başka bir fonksiyondan döndürüldü, bir nesneye özellik olarak yazıldı, sarmalanarak
davranışı değiştirildi. Bu kullanımların hepsi tek bir yetenekten çıkar — fonksiyonun da
bir değer olması. Sonraki ders bu yeteneği doğrudan ele alacak ve ölçüm kayıtları üzerinde
eşleme, süzme ve indirgeme işlemlerini kurarken bağlanma kurallarının nerede devreye
girdiğini gösterecek.

### Yüksek Mertebeden Fonksiyonlar

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/yuksek-mertebeden-fonksiyonlar

Fonksiyonun değer olarak kullanılması, eşleme–süzme–indirgeme zinciri, karşılaştırıcı üreten fonksiyonlar, bileşim ve davranış ekleyen sarmalayıcılar.

Önceki dersler fonksiyonu sürekli değer gibi kullandı: geri çağrı olarak aktarıldı, bir
fabrikadan döndürüldü, nesneye özellik olarak yazıldı, `bind` ile yenisi üretildi.
Hepsinin dayanağı tek bir olgudur — JavaScript'te fonksiyon bir nesnedir; çağrılabilir
olması dışında sayı ya da dizgiden farkı yoktur.

Fonksiyonu argüman olarak alan ya da fonksiyon döndüren fonksiyonlara **yüksek mertebeden
fonksiyon (higher-order function)** denir. Programlama Temelleri kursundaki Fonksiyonel
Programlamaya Giriş dersi kavramı tanıtmıştı. Bu ders onu ölçüm kayıtları üzerinde
uygular ve önceki dersteki bağlanma kurallarının bu kullanımda nereye değdiğini gösterir.

#### Fonksiyonu Argüman Olarak Vermek

En yaygın biçim, bir koleksiyon üzerindeki işlemin **ne yapacağını** dışarıdan almasıdır.
Süzme ölçütü, dönüştürme kuralı ve birikeç işlemi ayrı ayrı adlandırılmış fonksiyonlarda
tutulabilir.

```js
const kayitlar = [
  { sensor: "S-01", deger: 21.4, zaman: 1000 },
  { sensor: "S-02", deger: 19.8, zaman: 1060 },
  { sensor: "S-01", deger: 25.1, zaman: 1120 },
  { sensor: "S-03", deger: 18.2, zaman: 1180 },
  { sensor: "S-01", deger: 22.0, zaman: 1240 },
];

const esikUstunde = (kayit) => kayit.deger > 20;
const degeriAl = (kayit) => kayit.deger;
const topla = (birikec, deger) => birikec + deger;

const secilenler = kayitlar.filter(esikUstunde);
const degerler = secilenler.map(degeriAl);
const toplam = degerler.reduce(topla, 0);

console.log(secilenler.length);
console.log(degerler.join(","));
console.log(toplam.toFixed(1));
console.log((toplam / degerler.length).toFixed(3));

const sensoreGoreGrup = kayitlar.reduce((grup, kayit) => {
  (grup[kayit.sensor] ??= []).push(kayit.deger);
  return grup;
}, Object.create(null));

console.log(Object.keys(sensoreGoreGrup).join(","));
console.log(sensoreGoreGrup["S-01"].join(","));
console.log(typeof esikUstunde);
console.log(esikUstunde.length);
```

```
3
21.4,25.1,22
68.5
22.833
S-01,S-02,S-03
21.4,25.1,22
function
1
```

Süzme, eşleme ve indirgeme üç ayrı sorumluluğu ayırır: hangi kayıtların ilgilendiği, o
kayıtlardan hangi bilginin alındığı ve bilginin nasıl birleştirildiği. Her biri ayrı ayrı
sınanabilir ve başka bağlamlarda yeniden kullanılabilir.

Gruplama örneği indirgemenin genelliğini gösterir: birikeç bir sayı olmak zorunda değildir,
bir nesne de olabilir. Birikecin `Object.create(null)` ile kurulması bilinçlidir — sensör
adları veri kaynağından geldiği için, zincirden gelen adlarla çakışma riski böylece
ortadan kalkar.

Son iki satır fonksiyonun sıradan bir değer olduğunu doğrular: türü sorgulanabilir,
parametre sayısı okunabilir, bir değişkende saklanabilir.

#### Fonksiyon Döndüren Fonksiyonlar

İkinci biçim, bir fonksiyonu üreten fonksiyondur. Üretilen fonksiyon, üreticinin
parametrelerini kapanışla taşır; böylece davranışı dışarıdan ayarlanmış özel bir işlev
elde edilir.

```js
function anahtaraGore(anahtarIslevi, ters = false) {
  const yon = ters ? -1 : 1;
  return (a, b) => {
    const solAnahtar = anahtarIslevi(a);
    const sagAnahtar = anahtarIslevi(b);
    if (solAnahtar < sagAnahtar) return -1 * yon;
    if (solAnahtar > sagAnahtar) return 1 * yon;
    return 0;
  };
}

const kayitlar = [
  { sensor: "S-02", deger: 19.8 },
  { sensor: "S-01", deger: 25.1 },
  { sensor: "S-03", deger: 18.2 },
  { sensor: "S-01", deger: 21.4 },
];

const bicim = (liste) => liste.map((k) => `${k.sensor}:${k.deger}`).join(" ");

console.log(bicim([...kayitlar].sort(anahtaraGore((k) => k.deger))));
console.log(bicim([...kayitlar].sort(anahtaraGore((k) => k.deger, true))));
console.log(bicim([...kayitlar].sort(anahtaraGore((k) => k.sensor))));
console.log(bicim(kayitlar));
```

```
S-03:18.2 S-02:19.8 S-01:21.4 S-01:25.1
S-01:25.1 S-01:21.4 S-02:19.8 S-03:18.2
S-01:25.1 S-01:21.4 S-02:19.8 S-03:18.2
S-02:19.8 S-01:25.1 S-03:18.2 S-01:21.4
```

Tek bir `anahtaraGore` işlevi, üç farklı sıralama ölçütü üretir. Karşılaştırma mantığı bir
kez yazılır; değişen tek şey anahtarın nasıl çıkarıldığıdır. Bu, Algoritmalar kursundaki
Sıralama Algoritması Seçimi dersinde geçen bileşik anahtar fikrinin fonksiyon düzeyindeki
karşılığıdır.

Üçüncü satır sıralamanın **kararlılığını** gösterir: sensör adına göre sıralandığında iki
`S-01` kaydı, girdideki göreli sıralarını korur. Son satır ise kaynağın değişmediğini
doğrular — sıralama yerinde çalıştığı için her çağrıda yayma ile kopya alınmıştır.

#### Bileşim

Fonksiyon döndürmenin ikinci kullanımı, birden çok dönüşümü tek bir işleve
**bileştirmektir**. Her adım kendi başına yazılır, sıra bir kez kurulur.

```js
function kalibratorUret(kayma, carpan) {
  return (hamDeger) => (hamDeger + kayma) * carpan;
}

function birlestir(...islevler) {
  return (baslangic) => islevler.reduce((deger, islev) => islev(deger), baslangic);
}

const sensorS01 = kalibratorUret(-0.4, 1.02);
const sensorS02 = kalibratorUret(0.15, 1.0);

console.log(sensorS01(21.4).toFixed(3));
console.log(sensorS02(19.8).toFixed(3));

const yuvarla = (deger) => Math.round(deger * 10) / 10;
const santigrattanFahrenhayta = (deger) => deger * 1.8 + 32;

const boruHatti = birlestir(sensorS01, yuvarla, santigrattanFahrenhayta, yuvarla);

console.log(boruHatti(21.4));
console.log(boruHatti(19.8));
console.log(birlestir()(5));
```

```
21.420
19.950
70.5
67.6
5
```

`birlestir` işlevinin kendisi de bir indirgemedir: başlangıç değeri girdi, birikeç işlemi
"sıradaki işlevi uygula". Argümansız çağrıldığında birim işlevi üretir — hiçbir dönüşüm
uygulanmaz — ve bu, bileşimin doğru tanımlandığının göstergesidir.

`kalibratorUret` ile `bind` ile yapılan kısmi uygulama aynı sonucu verir. Fark okunurlukta
ve bağlamdadır: kapanışlı biçim `this` kullanmaz, hangi parametrenin sabitlendiği adından
bellidir ve bağlanma kurallarının hiçbirine tabi değildir.

#### Yöntemi Değer Olarak Aktarmak

Yüksek mertebeden bir fonksiyona bir **yöntem** verildiğinde, önceki derslerdeki kopma
sorunu geri döner: aktarılan şey fonksiyondur, bulunduğu nesne değil.

```js
class OlcumKaydi {
  constructor(sensor, deger) {
    this.sensor = sensor;
    this.deger = deger;
  }
  etiket() {
    return `${this.sensor}:${this.deger}`;
  }
}

const kayitlar = [new OlcumKaydi("S-01", 21.4), new OlcumKaydi("S-02", 19.8)];

try {
  console.log(kayitlar.map(OlcumKaydi.prototype.etiket).join(" "));
} catch (hata) {
  console.log(`dogrudan yontem: ${hata.constructor.name}`);
}

console.log(kayitlar.map((kayit) => kayit.etiket()).join(" "));
console.log(kayitlar.map(Function.prototype.call, OlcumKaydi.prototype.etiket).join(" "));

function uygulaHepsine(liste, islev, baglam) {
  const sonuc = [];
  for (const oge of liste) sonuc.push(islev.call(baglam, oge));
  return sonuc;
}

console.log(uygulaHepsine([1, 2, 3], function (sayi) {
  return sayi * this.carpan;
}, { carpan: 10 }).join(","));
```

```
dogrudan yontem: TypeError
S-01:21.4 S-02:19.8
S-01:21.4 S-02:19.8
10,20,30
```

Prototipteki yöntem doğrudan verildiğinde `this` bağlanmaz. En okunur çözüm çağrıyı bir ok
fonksiyonuyla sarmaktır. Üçüncü satır ise `map` işlevinin bağlam parametresini kullanan
daha dolaylı bir yoldur: eşlenen fonksiyon `call` olur, bağlam da asıl yöntem; her öge
`call` işleminin ilk argümanı olarak aktarılır. Yazımı kısa, okunması zordur; kısalık
uğruna okunurluktan vazgeçmenin iyi bir örneğidir.

Son bölüm, kendi yazdığınız yüksek mertebeden fonksiyonlarda bağlam desteğinin nasıl
verileceğini gösterir: `islev.call(baglam, oge)` biçimi, yerleşik dizi yöntemlerinin
ikinci parametresiyle aynı sözleşmeyi kurar.

#### Davranış Ekleyen Sarmalayıcılar

Üçüncü biçim, bir fonksiyonu alıp aynı arayüzü sunan ama davranışı genişletilmiş bir
fonksiyon döndürmektir. Kapanışlar dersindeki not alma sarmalayıcısı bunun bir örneğiydi;
kalıp geneldir.

```js
function birKere(islev) {
  let calisti = false;
  let sonuc;
  return function (...argumanlar) {
    if (!calisti) {
      calisti = true;
      sonuc = islev.apply(this, argumanlar);
    }
    return sonuc;
  };
}

function dogrulayarakSar(islev, gecerliMi, ileti) {
  return function (...argumanlar) {
    if (!argumanlar.every(gecerliMi)) throw new RangeError(ileti);
    return islev.apply(this, argumanlar);
  };
}

let kurulumSayisi = 0;
const kurulumYap = birKere((sensor) => {
  kurulumSayisi += 1;
  return `${sensor} kuruldu`;
});

console.log(kurulumYap("S-01"));
console.log(kurulumYap("S-02"));
console.log(kurulumSayisi);

const guvenliOrtalama = dogrulayarakSar(
  (...degerler) => degerler.reduce((t, d) => t + d, 0) / degerler.length,
  (deger) => typeof deger === "number" && Number.isFinite(deger),
  "tum degerler sonlu sayi olmalidir",
);

console.log(guvenliOrtalama(21.4, 19.8, 25.1).toFixed(3));
try {
  guvenliOrtalama(21.4, "yirmi");
} catch (hata) {
  console.log(`${hata.constructor.name}: ${hata.message}`);
}

const kayit = {
  sensor: "S-01",
  ozet: dogrulayarakSar(
    function (onEk) {
      return `${onEk}-${this.sensor}`;
    },
    (a) => typeof a === "string",
    "on ek dizgi olmalidir",
  ),
};
console.log(kayit.ozet("olcum"));
```

```
S-01 kuruldu
S-01 kuruldu
1
22.100
RangeError: tum degerler sonlu sayi olmalidir
olcum-S-01
```

İki sarmalayıcı da üç kurala uyar. Aynı parametreleri kabul ederler — bu yüzden kalan
parametreler kullanılır. Aynı dönüş değerini verirler. Ve bağlamı korurlar: gövdeleri ok
fonksiyonu değil işlev ifadesidir, `apply` çağrısında `this` aktarılır. Son bölüm bu
üçüncü kuralın sonucudur — sarmalanmış fonksiyon bir nesnenin yöntemi olarak
çağrıldığında `this` doğru nesneye bağlanır.

Sarmalayıcı ok fonksiyonuyla yazılsaydı `this` sarmalayıcının tanımlandığı kapsamdan
gelirdi ve yöntem olarak kullanılamazdı. Ok fonksiyonlarının sınırı burada da geçerlidir.

#### Özet

- Fonksiyon bir değerdir: değişkende saklanır, argüman olarak verilir, döndürülür ve
  özellik taşır.
- Süzme, eşleme ve indirgeme, bir koleksiyon üzerindeki işlemin ölçütünü dışarıdan alarak
  sorumlulukları ayırır; indirgemenin birikeci nesne de olabilir.
- Fonksiyon döndüren fonksiyonlar, parametreleri kapanışla taşıyan özelleşmiş işlevler
  üretir; sıralama ölçütleri ve kalibratörler bunun tipik örnekleridir.
- Bileşim, tek tek yazılmış dönüşümleri sırayla uygulayan bir indirgemedir.
- Yöntemi doğrudan yüksek mertebeden bir fonksiyona vermek bağı koparır; çağrıyı saran bir
  ok fonksiyonu en okunur çözümdür.
- Sarmalayıcılar aynı parametreleri ve dönüş değerini korumalı, `apply` ile bağlamı
  aktarmalıdır; bu yüzden ok fonksiyonu olarak yazılamazlar.

#### Sonraki Adım

Bu derste gruplama yapılırken birikeç `Object.create(null)` ile kuruldu; nedeni, sensör
adlarının zincirden gelen adlarla çakışabilmesiydi. Aynı önlem kursun ilk dersinde de,
kapanışlardaki önbellekte de gerekti. Nesneyi sözlük olarak kullanmanın bu ve başka
sınırları vardır: anahtarlar dizgiye çevrilir, sıra garantisi kısıtlıdır, eleman sayısı
doğrudan okunamaz. Sonraki konu, dilin bu iş için ayrılmış türlerini — anahtarlı ve tekil
koleksiyonları — tanıtarak başlayacak.

## İleri Koleksiyonlar

Anahtarlı ve tekil koleksiyonlar, zayıf referanslar, yineleme protokolü, üreteçlerle tembel diziler, ikili veri için tipli diziler ve derin kopyalama.

### Map ve Set

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/map-ve-set

Nesneyi sözlük olarak kullanmanın sınırları, anahtar kimliğini koruyan eşleme türü, tekil değer kümeleri ve iki kuruluşun gruplama üzerinde karşılaştırılması.

Kursun ilk dersinde prototipsiz nesne, sonraki derslerde de aynı önlem yeniden karşımıza
çıktı: kalibrasyon tablosunda, not alma önbelleğinde ve gruplama birikecinde. Her seferinde
neden aynıydı — anahtarları program dışından gelen bir eşlemede, nesnenin zincirinden gelen
adlar sonucu bozuyordu.

Bu, nesneyi sözlük olarak kullanmanın tek sınırı değil. Anahtarlar dizgiye çevrilir, sıra
kuralı beklenenden farklıdır ve eleman sayısı doğrudan okunamaz. Dil bu iş için iki tür
sunar: anahtar–değer çiftleri için `Map`, tekil değerler için `Set`. Ders önce sınırları
gösterir, sonra iki türü tanıtır.

#### Nesnenin Sözlük Olarak Sınırları

Nesne özelliklerinin anahtarı yalnızca dizgi ya da simge olabilir. Başka türden bir anahtar
verildiğinde dizgiye çevrilir.

```js
const sozluk = {};
const anahtarNesnesi = { sensor: "S-01" };
const digerNesne = { sensor: "S-02" };

sozluk[1] = "sayi bir";
sozluk["1"] = "dizgi bir";
sozluk[true] = "mantiksal";
sozluk[anahtarNesnesi] = "birinci nesne";
sozluk[digerNesne] = "ikinci nesne";

console.log(Object.keys(sozluk).join(" | "));
console.log(sozluk[1]);
console.log(sozluk[anahtarNesnesi]);
console.log(Object.keys(sozluk).length);

const sirali = {};
sirali["S-02"] = 1;
sirali[10] = 2;
sirali["S-01"] = 3;
sirali[2] = 4;
console.log(Object.keys(sirali).join(","));

const kirli = { sensor: "S-01" };
console.log("toString" in kirli);
console.log(Object.hasOwn(kirli, "toString"));
console.log(typeof kirli["toString"]);
```

```
1 | true | [object Object]
dizgi bir
ikinci nesne
3
2,10,S-02,S-01
true
false
function
```

Üç sınır görünür durumda. Birincisi, `1` ile `"1"` aynı anahtardır; ikinci atama birinciyi
ezmiştir. İkincisi daha ağırdır: iki farklı nesne aynı `"[object Object]"` dizgisine
çevrildiği için tek bir anahtar hâline gelir — nesneler anahtar olarak kullanılamaz.
Üçüncüsü sıradır: tam sayıya benzeyen anahtarlar artan sayısal düzende öne alınır, diğerleri
ekleme sırasını korur.

Son üç satır tanıdık sorunu yineler. Sözlükte `toString` diye bir anahtar yoktur ama sorgu
onu bulur; anahtarların dışarıdan geldiği her durumda `Object.hasOwn` ya da prototipsiz
nesne kullanılmalıdır.

#### Anahtar Kimliğini Koruyan Eşleme

`Map`, anahtarları dönüştürmez. Her tür değer anahtar olabilir ve karşılaştırma kimlik
üzerinden yapılır.

```js
const anahtarNesnesi = { sensor: "S-01" };
const digerNesne = { sensor: "S-02" };

const defter = new Map();
defter.set(1, "sayi bir");
defter.set("1", "dizgi bir");
defter.set(anahtarNesnesi, "birinci nesne");
defter.set(digerNesne, "ikinci nesne");
defter.set(NaN, "tanimsiz olcum");

console.log(defter.size);
console.log(defter.get(1));
console.log(defter.get("1"));
console.log(defter.get(anahtarNesnesi));
console.log(defter.get({ sensor: "S-01" }));
console.log(defter.get(NaN));
console.log(defter.has(digerNesne));

defter.delete(digerNesne);
console.log(defter.size);

const anahtarTurleri = [...defter.keys()].map((a) => typeof a).join(",");
console.log(anahtarTurleri);

const sirali = new Map();
sirali.set("S-02", 1);
sirali.set(10, 2);
sirali.set("S-01", 3);
sirali.set(2, 4);
console.log([...sirali.keys()].join(","));
```

```
5
sayi bir
dizgi bir
birinci nesne
undefined
tanimsiz olcum
true
4
number,string,object,number
S-02,10,S-01,2
```

`1` ile `"1"` ayrı anahtarlardır. Nesne anahtarı **kimliğiyle** saklanır: aynı içerikli
başka bir nesneyle sorgulandığında sonuç bulunamaz. Programlama Temelleri kursundaki
Değişkenler ve Bağlama dersindeki kimlik–eşitlik ayrımı burada doğrudan işler.

`NaN` anahtarının bulunabilmesi bir ayrıntıya işaret eder: karşılaştırma katı eşitlikle
değil, `NaN` değerini kendisine eşit sayan bir kuralla yapılır. Aynı kural `Set` için de
geçerlidir.

Son satır sıra kuralını gösterir: `Map` yalnızca ekleme sırasını korur, sayısal anahtarları
öne almaz. `size` özelliği eleman sayısını doğrudan verir; nesnede aynı bilgi için tüm
anahtarların listelenmesi gerekir.

Karma tablosu ile ilişkisi de burada kurulur. Veri Yapıları kursundaki Karma Tabloları
dersinde incelenen ortalama sabit maliyetli erişim, hem nesne özelliklerinin hem de
`Map` yapısının altında yatan davranıştır; `Map` bunun anahtar kimliğini koruyan biçimidir.

#### Dönüşümler ve Gezinme

`Map` ile nesne arasında iki yönlü dönüşüm yerleşiktir. Dizileştirmede ise fark önemlidir.

```js
const kayitNesnesi = { "S-01": 21.4, "S-02": 19.8, "S-03": 18.2 };

const nesnedenMap = new Map(Object.entries(kayitNesnesi));
console.log(nesnedenMap.size);
console.log(nesnedenMap.get("S-02"));

const geriNesne = Object.fromEntries(nesnedenMap);
console.log(JSON.stringify(geriNesne));

console.log(JSON.stringify(nesnedenMap));
console.log(JSON.stringify([...nesnedenMap]));

const ciftlerden = new Map([
  ["S-01", 21.4],
  ["S-02", 19.8],
]);
console.log([...ciftlerden.entries()].map(([a, d]) => `${a}=${d}`).join(" "));

let toplam = 0;
for (const [, deger] of nesnedenMap) toplam += deger;
console.log(toplam.toFixed(1));

nesnedenMap.forEach((deger, anahtar) => {
  if (anahtar === "S-01") console.log(`forEach: ${anahtar} -> ${deger}`);
});
```

```
3
19.8
{"S-01":21.4,"S-02":19.8,"S-03":18.2}
{}
[["S-01",21.4],["S-02",19.8],["S-03",18.2]]
S-01=21.4 S-02=19.8
59.4
forEach: S-01 -> 21.4
```

Dördüncü satır kritiktir: `JSON.stringify` bir `Map` nesnesini boş nesne olarak
dizileştirir. Neden ilk derste kurulmuştu — dizileştirme kendi **sayılabilir
özelliklerle** çalışır, `Map` içeriği ise özellik olarak tutulmaz. Verinin taşınması
gerekiyorsa çiftler dizisine çevrilmelidir. Aynı sorunun genel çözümü kursun son dersinde
ele alınacak.

`for...of` döngüsü her adımda bir çift verir; ayrıştırmayla anahtar ve değer ayrılabilir.
`forEach` yönteminde argüman sırası ters olduğu için — önce değer, sonra anahtar —
karıştırılmaya açıktır.

#### Tekil Değerler

`Set`, aynı değeri birden çok kez tutmayan bir koleksiyondur. Veri Yapıları kursundaki
Kümeler ve Çok Kümeler dersinde tanıtılan kümenin dildeki karşılığıdır.

```js
const okunanSensorler = ["S-01", "S-02", "S-01", "S-03", "S-02", "S-01"];

const tekiller = new Set(okunanSensorler);
console.log(tekiller.size);
console.log([...tekiller].join(","));
console.log(tekiller.has("S-02"));
console.log(tekiller.has("S-99"));

const sayiKumesi = new Set([1, 1, NaN, NaN, 0, -0]);
console.log(sayiKumesi.size);
console.log([...sayiKumesi].join(","));

const nesneKumesi = new Set([{ sensor: "S-01" }, { sensor: "S-01" }]);
console.log(nesneKumesi.size);

const bakimdakiler = new Set(["S-02", "S-04"]);
const kesisim = [...tekiller].filter((s) => bakimdakiler.has(s));
const fark = [...tekiller].filter((s) => !bakimdakiler.has(s));
const birlesim = [...new Set([...tekiller, ...bakimdakiler])];

console.log(kesisim.join(","));
console.log(fark.join(","));
console.log(birlesim.join(","));
```

```
3
S-01,S-02,S-03
true
false
3
1,NaN,0
2
S-02
S-01,S-03
S-01,S-02,S-03,S-04
```

Tekilleştirme tek satıra iner ve ekleme sırası korunur. Sayısal örnek karşılaştırma
kuralını netleştirir: iki `NaN` değeri aynı sayılır — katı eşitlikte olmayan bir davranış —
buna karşılık `0` ile `-0` da aynı sayılır. Nesneler yine kimlikleriyle karşılaştırılır;
aynı içerikli iki nesne ayrı elemanlardır.

Küme işlemleri, `has` sorgusunun ortalama sabit maliyetli olmasından yararlanır. Aynı
işlemi dizilerle yapmak, her eleman için doğrusal arama gerektirir ve maliyeti iki
kümenin boyutlarının çarpımına çıkarır.

#### Gruplamada Karşılaştırma

Yüksek Mertebeden Fonksiyonlar dersindeki gruplama örneği, iki kuruluşu yan yana koymak
için uygun bir sınamadır. Sensör adları veri kaynağından geldiğine göre, aralarında
`constructor` gibi bir ad da bulunabilir.

```js
const kayitlar = [
  { sensor: "S-01", deger: 21.4 },
  { sensor: "S-02", deger: 19.8 },
  { sensor: "S-01", deger: 25.1 },
  { sensor: "constructor", deger: 0.5 },
];

function naifGrupla(liste) {
  const grup = {};
  for (const kayit of liste) {
    if (!grup[kayit.sensor]) grup[kayit.sensor] = [];
    grup[kayit.sensor].push(kayit.deger);
  }
  return grup;
}

function nesneyleGrupla(liste) {
  const grup = {};
  for (const kayit of liste) {
    if (!Array.isArray(grup[kayit.sensor])) grup[kayit.sensor] = [];
    grup[kayit.sensor].push(kayit.deger);
  }
  return grup;
}

function mapIleGrupla(liste) {
  const grup = new Map();
  for (const kayit of liste) {
    if (!grup.has(kayit.sensor)) grup.set(kayit.sensor, []);
    grup.get(kayit.sensor).push(kayit.deger);
  }
  return grup;
}

try {
  naifGrupla(kayitlar);
} catch (hata) {
  console.log(`naif surum: ${hata.constructor.name}`);
}

const nesneGrubu = nesneyleGrupla(kayitlar);
const mapGrubu = mapIleGrupla(kayitlar);

console.log(Object.keys(nesneGrubu).join(","));
console.log([...mapGrubu.keys()].join(","));
console.log(nesneGrubu["S-01"].join(","));
console.log(mapGrubu.get("S-01").join(","));
console.log(nesneGrubu["constructor"].join(","));
console.log(mapGrubu.get("constructor").join(","));
console.log(Object.keys(nesneGrubu).length === mapGrubu.size);
```

```
naif surum: TypeError
S-01,S-02,constructor
S-01,S-02,constructor
21.4,25.1
21.4,25.1
0.5
0.5
true
```

Naif sürüm hata verir: `grup["constructor"]` ifadesi zincirden gelen fonksiyonu bulur, bu
değer doğru sayıldığı için dizi hiç yaratılmaz ve fonksiyona `push` çağrılır. Düzeltilmiş
sürüm doğrulamayı tür denetimiyle yaptığı için çalışır — ama düzeltmenin gerekli olduğunu
bilmek gerekir.

`Map` ile yazılan sürümde böyle bir tuzak yoktur; `has` sorgusu zincire bakmaz, çünkü
`Map` içeriği özellik olarak tutulmaz. Kural olarak: **anahtarlar sabit ve programcı
tarafından yazılmışsa nesne, veri kaynağından geliyorsa `Map` kullanılır.**

#### Özet

- Nesne anahtarları dizgiye çevrilir; farklı nesneler aynı anahtara düşer ve tam sayıya
  benzeyen anahtarlar sıralamada öne alınır.
- `Map` anahtarları dönüştürmez, kimlikleriyle saklar, ekleme sırasını korur ve `size` ile
  eleman sayısını doğrudan verir.
- `NaN` değeri hem `Map` hem `Set` içinde kendisine eşit sayılır; nesneler kimlikleriyle
  karşılaştırılır.
- `JSON.stringify` bir `Map` nesnesini boş nesne olarak dizileştirir; içerik çiftler
  dizisine çevrilmelidir.
- `Set` tekilleştirmeyi tek adıma indirir; küme işlemleri sabit maliyetli sorgudan
  yararlanır.
- Anahtarlar veri kaynağından geliyorsa `Map`, sabit ve programda yazılıysa nesne
  kullanılır.

#### Sonraki Adım

`Map` bir anahtarı sakladığı sürece, o anahtar nesnesi bellekte kalır. Bir nesneye ek
bilgi iliştirmek için kurulan tablolar bu yüzden nesnelerin ömrünü uzatır: tablodan
silinmeyen her giriş, artık kullanılmayan bir nesneyi yaşatır. Sonraki ders, tuttukları
nesnelerin toplanmasını engellemeyen zayıf koleksiyonları ve bunların özel veri saklamada
oynadığı rolü ele alacak.

### Zayıf Koleksiyonlar

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/zayif-koleksiyonlar

Anahtarlarının toplanmasını engellemeyen eşleme ve küme türleri, nesneye dışarıdan veri iliştirme kalıbı, gezinilemezliğin nedeni ve zayıf başvurular.

Önceki ders `Map` ile biterken bir sorun bıraktı: bir eşleme bir nesneyi anahtar olarak
sakladığı sürece o nesne erişilebilir kalır. Nesneye ek bilgi iliştirmek için kurulan
tablolar bu yüzden sızıntı kaynağıdır — tablodan silinmeyen her giriş, artık
kullanılmayan bir nesneyi yaşatır.

`WeakMap` ve `WeakSet` bu sorunu çözer. Tuttukları nesnelere yaptıkları başvuru
**zayıftır**: başka hiçbir yerden erişilemeyen bir nesne, yalnızca zayıf koleksiyonda
bulunduğu için yaşamaz. Karşılığında birkaç yetenek geri verilir; ders bu ödünleşimi
inceler.

#### Zayıf Eşleme

`WeakMap`, `Map` yapısının kısıtlanmış bir biçimidir: anahtarları yalnızca nesne (ve
ES2023'ten beri kayıtlı olmayan simge) olabilir, eleman sayısı okunamaz ve içeriği
gezilemez.

```js
const kayit = { sensor: "S-01", deger: 21.4 };
const digerKayit = { sensor: "S-02", deger: 19.8 };

const zayifDefter = new WeakMap();
zayifDefter.set(kayit, { kaynakDosya: "olcum-2.csv", satir: 17 });
zayifDefter.set(digerKayit, { kaynakDosya: "olcum-3.csv", satir: 4 });

console.log(zayifDefter.has(kayit));
console.log(zayifDefter.get(kayit).satir);
console.log(zayifDefter.get({ sensor: "S-01", deger: 21.4 }));

try {
  zayifDefter.set("S-01", { satir: 1 });
} catch (hata) {
  console.log(`ilkel anahtar: ${hata.constructor.name}`);
}

console.log(zayifDefter.size);
console.log(typeof zayifDefter.keys);
console.log(typeof zayifDefter[Symbol.iterator]);

try {
  for (const _ of zayifDefter) console.log(_);
} catch (hata) {
  console.log(`gezinme: ${hata.constructor.name}`);
}

console.log(zayifDefter.delete(kayit));
console.log(zayifDefter.has(kayit));
console.log(JSON.stringify(zayifDefter));
```

```
true
17
undefined
ilkel anahtar: TypeError
undefined
undefined
undefined
gezinme: TypeError
true
false
{}
```

Anahtar kimliği kuralı `Map` ile aynıdır: aynı içerikli başka bir nesne farklı bir
anahtardır. Kayıtlı olmayan simge dışındaki bir ilkel değer anahtar olarak verildiğinde
hata yükselir, çünkü o değerler için "toplanabilirlik" tanımlı değildir — dizgiler
paylaşılan değerlerdir, kimliği izlenen nesneler değil.

Eksik olan üç yetenek — `size`, `keys` ve gezinme — bir eksiklik değil, doğrudan
tasarımın sonucudur. İçeriğin listelenebilmesi, çöp toplayıcının ne zaman çalıştığını
programın gözlemleyebilmesi anlamına gelirdi; aynı program iki kez çalıştırıldığında farklı
sonuç verebilirdi. Gezinilemezlik, belirlenimciliği korumak için ödenen bedeldir.

#### Nesneye Dışarıdan Veri İliştirmek

Zayıf eşlemenin en yaygın kullanımı, bir nesneye ait bilgiyi nesnenin **dışında**
tutmaktır. Sınıf sözdizimindeki özel alanlar bu ihtiyacın dil düzeyindeki karşılığıdır;
zayıf eşleme aynı sonucu kütüphane koduyla üretir.

```js
const ozelVeri = new WeakMap();

class OlcumKaydi {
  constructor(sensor, hamDeger, kayma) {
    this.sensor = sensor;
    ozelVeri.set(this, { hamDeger, kayma, okumaSayisi: 0 });
  }

  get okunanDeger() {
    const veri = ozelVeri.get(this);
    veri.okumaSayisi += 1;
    return veri.hamDeger + veri.kayma;
  }

  denetim() {
    return `okuma=${ozelVeri.get(this).okumaSayisi}`;
  }

  static veriTasiyorMu(nesne) {
    return ozelVeri.has(nesne);
  }
}

const kayit = new OlcumKaydi("S-01", 21.4, -0.4);

console.log(kayit.okunanDeger);
console.log(kayit.okunanDeger);
console.log(kayit.denetim());
console.log(Object.keys(kayit).join(","));
console.log(JSON.stringify(kayit));
console.log(OlcumKaydi.veriTasiyorMu(kayit));
console.log(OlcumKaydi.veriTasiyorMu({ sensor: "S-02" }));

const kopukDenetim = kayit.denetim;
try {
  kopukDenetim();
} catch (hata) {
  console.log(`kopuk cagri: ${hata.constructor.name}`);
}
```

```
21
21
okuma=2
sensor
{"sensor":"S-01"}
true
false
kopuk cagri: TypeError
```

Ham değer, kayma ve okuma sayacı kaydın özelliği değildir: listelenmez, dizileştirilmez ve
`ozelVeri` başvurusuna erişemeyen kod tarafından okunamaz. Sonuç, Sınıf Sözdizimi
dersindeki özel alanlarla ve Kapanışlar dersindeki fabrika kuruluşuyla aynıdır.

Üç yolun farkı nerede saklandığındadır. Özel alan nesnenin içinde, kapanış kapsamda, zayıf
eşleme ise nesnenin dışında ama nesneye anahtarlanmış olarak tutar. Üçüncüsünün ayırt edici
yanı, **nesneyi değiştirmeden** veri iliştirebilmesidir: sınıfını yazmadığınız, hatta
dondurulmuş bir nesneye bile bu yolla bilgi bağlanabilir.

Son satır ortak sınırı yineler: veri `this` üzerinden bulunduğu için, yöntem nesnesinden
koparıldığında yine hata verir. Zayıf eşleme bağlanma kurallarını değiştirmez.

#### Zayıf Küme

`WeakSet` aynı ilkeyi kümeye uygular: nesneleri işaretlemek için kullanılır, içeriği
gezilemez.

```js
const islenmisler = new WeakSet();

function isle(kayit) {
  if (islenmisler.has(kayit)) return "zaten islendi";
  islenmisler.add(kayit);
  return `islendi: ${kayit.sensor}`;
}

const birinci = { sensor: "S-01", deger: 21.4 };
const ikinci = { sensor: "S-02", deger: 19.8 };

console.log(isle(birinci));
console.log(isle(birinci));
console.log(isle(ikinci));
console.log(islenmisler.has(birinci));
console.log(islenmisler.has({ sensor: "S-01", deger: 21.4 }));

try {
  islenmisler.add("S-03");
} catch (hata) {
  console.log(`ilkel deger: ${hata.constructor.name}`);
}

console.log(islenmisler.size);
console.log(typeof islenmisler.values);
```

```
islendi: S-01
zaten islendi
islendi: S-02
true
false
ilkel deger: TypeError
undefined
undefined
```

Kullanım kalıbı tektir: "bu nesne daha önce işlendi mi?" Değişmezlik Teknikleri dersindeki
derin dondurma işlevinde ziyaret işaretlemesi için `WeakSet` seçilmesinin nedeni buydu —
işaretleme geçicidir ve gezilen nesnelerin ömrünü uzatmamalıdır.

Nesne kimliğine dayalı çevrim algılama, yinelenen işlemi engelleme ve bir nesnenin belirli
bir yapıcıyla üretilip üretilmediğini sınama, aynı kalıbın farklı adlarıdır.

#### Güçlü Başvurunun Bedeli

İki koleksiyon türünün bellek üzerindeki farkı, çöp toplama gözlemlenmeden de görülebilir:
güçlü eşleme içeriğini sayar ve listeler, zayıf eşleme sayamaz.

```js
const gucluOnbellek = new Map();
const zayifOnbellek = new WeakMap();

function olcumIsle(kayit, onbellek) {
  if (onbellek.has(kayit)) return onbellek.get(kayit);
  const sonuc = `${kayit.sensor}:${kayit.deger.toFixed(1)}`;
  onbellek.set(kayit, sonuc);
  return sonuc;
}

for (let sira = 0; sira < 1000; sira += 1) {
  const gecici = { sensor: `S-${sira}`, deger: sira / 10 };
  olcumIsle(gecici, gucluOnbellek);
  olcumIsle(gecici, zayifOnbellek);
}

console.log(gucluOnbellek.size);
console.log([...gucluOnbellek.values()][0]);
console.log([...gucluOnbellek.keys()][999].sensor);
console.log(typeof zayifOnbellek.size);

const kalici = { sensor: "S-K", deger: 30 };
console.log(olcumIsle(kalici, zayifOnbellek));
console.log(zayifOnbellek.has(kalici));
```

```
1000
S-0:0.0
S-999
undefined
S-K:30.0
true
```

Döngüdeki bin geçici kayıt, döngü bittiğinde program açısından kullanılmaz durumdadır —
`gecici` değişkeni her yinelemede yeniden bağlanır ve son yinelemeden öncekilere hiçbir
yerden erişilemez. Buna karşın güçlü eşleme hepsini anahtar olarak tuttuğu için bin nesne
de, bin sonuç dizgisi de erişilebilir kalır; `keys` çağrısı bunu doğrudan gösterir.

Zayıf eşlemede aynı girişler yalnızca anahtar nesne başka bir yerden erişilebilir olduğu
sürece anlamlıdır. Son iki satır bunu gösterir: `kalici` nesnesi bir değişkende tutulduğu
için giriş bulunur. Erişilemez hâle gelen anahtarların girişlerinin ne zaman temizleneceği
ise belirtimde zamanlanmış değildir ve program tarafından gözlemlenemez; bu dersteki
hiçbir çıktı toplama anına bağlı değildir.

Erişilebilirlik temelli çöp toplamanın nasıl çalıştığı, Eşzamansız JavaScript ve Çalışma
Zamanı kursunun konusudur.

#### Zayıf Başvurular

Zayıf koleksiyonların yanında iki yapı daha aynı ilkeyi tek bir nesne için sunar.
`WeakRef` bir nesneye zayıf başvuru tutar; `deref` çağrısı nesne hâlâ erişilebilirse onu,
değilse `undefined` verir. `FinalizationRegistry` ise bir nesne toplandıktan sonra
çalıştırılacak bir geri çağrı kaydeder.

```js
const kayit = { sensor: "S-01", deger: 21.4 };
const zayifBasvuru = new WeakRef(kayit);

console.log(zayifBasvuru.deref() === kayit);
console.log(zayifBasvuru.deref().sensor);

const kutuk = [];
const kayitDefteri = new FinalizationRegistry((etiket) => {
  kutuk.push(etiket);
});
kayitDefteri.register(kayit, "S-01 toplandi");

console.log(typeof kayitDefteri.register);
console.log(typeof kayitDefteri.unregister);
console.log(kutuk.length);
console.log(zayifBasvuru.deref() !== undefined);
```

```
true
S-01
function
function
0
true
```

Çıktının son iki satırı, `kayit` değişkeni yaşadığı sürece geçerlidir: kütük boştur ve
başvuru çözülebilir. Geri çağrının **ne zaman** çalışacağı — hatta program sonlanmadan
önce çalışıp çalışmayacağı — belirtimde güvence altına alınmamıştır. Bu yüzden bu iki
yapı program mantığının dayanağı yapılmaz; kullanım alanları tanı, ölçüm ve isteğe bağlı
önbelleklerdir.

Aynı nedenle, bu dersin hiçbir bloğunda toplama anına bağlı bir çıktı gösterilmemiştir:
böyle bir çıktı çalışma zamanına ve o anki bellek durumuna göre değişir.

#### Özet

- Zayıf koleksiyonlar tuttukları nesnelere zayıf başvuru yapar; yalnızca orada bulunmak bir
  nesneyi yaşatmaz.
- `WeakMap` ve `WeakSet` yalnızca nesne ve kayıtlı olmayan simge kabul eder; `size`,
  gezinme ve dizileştirme desteklemez — bu kısıt belirlenimciliği korur.
- Zayıf eşleme, nesneyi hiç değiştirmeden ona veri iliştirir; özel alan ve kapanışla
  kurulan gizlemenin üçüncü seçeneğidir.
- Zayıf küme, nesneleri işaretlemek için kullanılır: çevrim algılama, yinelenen işlemi
  engelleme, üyelik sınama.
- Güçlü bir eşleme anahtarlarını erişilebilir tutar; nesneye anahtarlanmış önbelleklerde
  bu, sınırsız büyümenin kaynağıdır.
- `WeakRef` ve `FinalizationRegistry` zamanlaması belirtimde güvence altına alınmadığı için
  program mantığının dayanağı yapılmaz.

#### Sonraki Adım

Bu derste `WeakMap` üzerinde `for...of` denemesi hata verdi, oysa aynı döngü `Map`, `Set`,
dizi ve dizgi üzerinde çalışıyor. Aradaki fark bir tür listesi değil, tek bir sözleşmedir:
bir nesnenin gezilebilir olması, belirli bir yöntemi sunmasına bağlıdır. Sonraki ders bu
sözleşmeyi — yineleme protokolünü — açacak ve ölçüm serisine kendi gezinme davranışını
kazandıracak.

### Yineleyiciler

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/yineleyiciler

Yineleme protokolünün iki sözleşmesi, bir türü prototip üzerinden gezilebilir kılmak, yineleyici ile gezilebilir ayrımı, erken çıkışta temizlik ve tembel üretim.

Önceki derste `WeakMap` üzerinde `for...of` denemesi hata verdi, oysa aynı döngü `Map`,
`Set`, dizi ve dizgi üzerinde çalışıyor. Aradaki fark bir tür listesi değildir: `for...of`
döngüsü, yayma işleci, ayrıştırma ve `Array.from` hepsi tek bir sözleşmeye bakar.

**Yineleme protokolü (iteration protocol)** iki sözleşmeden oluşur. Bir nesne
`[Symbol.iterator]` adlı bir yöntem sunuyorsa **gezilebilirdir (iterable)**; bu yöntem,
`next` sunan bir **yineleyici (iterator)** döndürmelidir. Ders önce sözleşmeyi elle kurar,
sonra ölçüm serisini bu sözleşmeye bağlar.

#### next Sözleşmesi

Yineleyici, tek bir yöntemi olan sıradan bir nesnedir. `next` çağrısı iki alanlı bir nesne
döndürür: sıradaki değer ve dizinin bitip bitmediği.

```js
function olcumYineleyicisi(degerler) {
  let sira = 0;
  return {
    next() {
      if (sira >= degerler.length) return { value: undefined, done: true };
      const deger = degerler[sira];
      sira += 1;
      return { value: deger, done: false };
    },
  };
}

const yineleyici = olcumYineleyicisi([21.4, 19.8, 25.1]);

console.log(JSON.stringify(yineleyici.next()));
console.log(JSON.stringify(yineleyici.next()));
console.log(JSON.stringify(yineleyici.next()));
console.log(JSON.stringify(yineleyici.next()));

const ikinci = olcumYineleyicisi([21.4, 19.8]);
let adim = ikinci.next();
const toplananlar = [];
while (!adim.done) {
  toplananlar.push(adim.value);
  adim = ikinci.next();
}
console.log(toplananlar.join(","));
console.log(typeof yineleyici[Symbol.iterator]);
```

```
{"value":21.4,"done":false}
{"value":19.8,"done":false}
{"value":25.1,"done":false}
{"done":true}
21.4,19.8
undefined
```

Konum bilgisi `sira` değişkeninde tutulur ve dışarıdan görünmez — Kapanışlar dersindeki
kalıp burada da işler. Dördüncü çıktıda `value` alanının görünmemesi dizileştirmenin
sonucudur: `undefined` değerli alanlar `JSON.stringify` çıktısına girmez.

`while` döngüsü, `for...of` döngüsünün elle yazılmış hâlidir. Son satır ise bu nesnenin
neden `for...of` ile kullanılamayacağını söyler: `next` sunuyor ama `[Symbol.iterator]`
sunmuyor, yani yineleyicidir ama gezilebilir değildir.

#### Bir Türü Gezilebilir Kılmak

İkinci sözleşme, bir nesnenin `[Symbol.iterator]` yöntemi sunmasıdır. Yöntem sınıf gövdesine
yazıldığında prototipte durur ve tüm örnekler gezilebilir olur.

```js
class OlcumSerisi {
  constructor(sensor, degerler) {
    this.sensor = sensor;
    this.degerler = degerler;
  }

  [Symbol.iterator]() {
    let sira = 0;
    const degerler = this.degerler;
    return {
      next() {
        if (sira >= degerler.length) return { value: undefined, done: true };
        const deger = degerler[sira];
        sira += 1;
        return { value: deger, done: false };
      },
    };
  }
}

const seri = new OlcumSerisi("S-01", [21.4, 19.8, 25.1]);

const toplananlar = [];
for (const deger of seri) toplananlar.push(deger);
console.log(toplananlar.join(","));

console.log([...seri].join(","));
console.log(Array.from(seri).length);

const [ilk, ikinci] = seri;
console.log(`${ilk} ${ikinci}`);

console.log(Math.max(...seri));
console.log(Object.hasOwn(seri, Symbol.iterator));
console.log(Object.hasOwn(OlcumSerisi.prototype, Symbol.iterator));
console.log(Object.keys(seri).join(","));
console.log(JSON.stringify(seri));
```

```
21.4,19.8,25.1
21.4,19.8,25.1
3
21.4 19.8
25.1
false
true
sensor,degerler
{"sensor":"S-01","degerler":[21.4,19.8,25.1]}
```

Tek bir yöntem eklendi; beş ayrı dil özelliği çalışır duruma geldi. `for...of`, yayma,
`Array.from`, dizi ayrıştırması ve argüman yayma hepsi aynı sözleşmeyi kullanır.

Altıncı ve yedinci satırlar dersin ekseniyle bağı kurar: yöntem örnekte değil,
`OlcumSerisi.prototype` üzerindedir. Gezilebilirlik nesneye ait bir bayrak değil, zincirde
bulunan bir yöntemdir. Bu yüzden bir türü sonradan gezilebilir kılmak, prototipine bu
yöntemi eklemekle mümkündür.

Simge anahtarlı olduğu için `Object.keys` çıktısında görünmez ve dizileştirmeye girmez;
Özellik Tanımlayıcıları dersinde geçen simge anahtarların bu özelliği, protokol
yöntemlerini veri alanlarından ayrı tutmayı sağlar.

#### Yineleyici ile Gezilebilir Ayrımı

İki sözleşme çoğu zaman aynı nesnede birleşir. Yerleşik yineleyiciler kendilerini
döndüren bir `[Symbol.iterator]` yöntemi sunar; bu da onların hem yineleyici hem
gezilebilir olmasını sağlar.

```js
const degerler = [21.4, 19.8, 25.1];

const yineleyici = degerler[Symbol.iterator]();
console.log(typeof yineleyici.next);
console.log(typeof yineleyici[Symbol.iterator]);
console.log(yineleyici[Symbol.iterator]() === yineleyici);

console.log(JSON.stringify(yineleyici.next()));

const kalanlar = [];
for (const deger of yineleyici) kalanlar.push(deger);
console.log(kalanlar.join(","));

const bosDonus = [];
for (const deger of yineleyici) bosDonus.push(deger);
console.log(bosDonus.length);

const birinciTur = [];
for (const deger of degerler) birinciTur.push(deger);
const ikinciTur = [];
for (const deger of degerler) ikinciTur.push(deger);
console.log(birinciTur.length === ikinciTur.length);

const dizgi = "olcum";
console.log([...dizgi].join("-"));
const harita = new Map([["S-01", 21.4]]);
console.log([...harita][0].join("="));
```

```
function
function
true
{"value":21.4,"done":false}
19.8,25.1
0
true
o-l-c-u-m
S-01=21.4
```

Ayrımın pratik sonucu tükenmedir. Yineleyici üzerinde gezinmek onu **tüketir**: ilk
`for...of` kalan iki değeri okur; ikincisi hiçbir şey bulamaz. Gezilebilir nesne ise her
gezinmede yeni bir yineleyici üretir, bu yüzden defalarca gezilebilir.

Bu, bir işleve gezilebilir bir nesne mi yoksa yineleyici mi verildiğinin neden önemli
olduğunu açıklar. İki kez gezmesi gereken bir işleve yineleyici verilirse ikinci geçiş
boş döner; hata mesajı da oluşmaz.

Son iki satır protokolün ne kadar yaygın olduğunu gösterir. Dizgi üzerinde gezinmek kod
birimi değil, kod noktası verir — Bilgisayarlar Nasıl Çalışır kursundaki Karakter
Kodlamaları dersinde tanıtılan ayrım burada gözlenebilir. `Map` üzerinde gezinmek ise
anahtar–değer çiftleri verir.

#### Erken Çıkış ve Temizlik

`for...of` döngüsü `break` ile sonlandırıldığında ya da bir hata yükseldiğinde, protokol
yineleyicinin `return` yöntemini çağırır. Bu, açık kalan kaynakların kapatılması için
ayrılmış noktadır.

```js
const kutuk = [];

function kaynakYineleyicisi(degerler) {
  let sira = 0;
  let kapali = false;
  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      if (kapali || sira >= degerler.length) return { value: undefined, done: true };
      const deger = degerler[sira];
      sira += 1;
      return { value: deger, done: false };
    },
    return(deger) {
      kapali = true;
      kutuk.push(`kaynak kapatildi, okunan=${sira}`);
      return { value: deger, done: true };
    },
  };
}

const kaynak = kaynakYineleyicisi([21.4, 19.8, 25.1, 18.2]);

for (const deger of kaynak) {
  if (deger > 20 && deger < 22) {
    console.log(`bulundu: ${deger}`);
    break;
  }
}

console.log(kutuk.join(" | "));
console.log(JSON.stringify(kaynak.next()));

const ikinciKaynak = kaynakYineleyicisi([21.4, 19.8]);
const [tekDeger] = ikinciKaynak;
console.log(tekDeger);
console.log(kutuk.length);
```

```
bulundu: 21.4
kaynak kapatildi, okunan=1
{"done":true}
21.4
2
```

`break` deyimi yalnızca döngüyü kırmaz, yineleyiciye de haber verir. Kapatılan yineleyici
sonraki `next` çağrılarında bitmiş olarak yanıt verir. Son bölüm, aynı çağrının dizi
ayrıştırmasında da yapıldığını gösterir: istenen sayıda değer alındıktan sonra kaynak
kapatılır.

Bu davranış, dosya ve ağ akışları gibi kaynaklara bağlı yineleyicilerde önemlidir; erken
çıkış, kaynağın açık kalmasına yol açmaz.

#### Tembel Üretim

Protokolün son özelliği, değerlerin **istendikçe** üretilmesidir. Kaynak dizi olsa bile,
her değer üzerinde yapılan hesap yalnızca o değer okunduğunda çalışır.

```js
let hesapSayisi = 0;

function kalibreliSeri(hamDegerler, kayma) {
  return {
    [Symbol.iterator]() {
      let sira = 0;
      return {
        [Symbol.iterator]() {
          return this;
        },
        next() {
          if (sira >= hamDegerler.length) return { value: undefined, done: true };
          hesapSayisi += 1;
          const deger = hamDegerler[sira] + kayma;
          sira += 1;
          return { value: Number(deger.toFixed(2)), done: false };
        },
      };
    },
  };
}

const seri = kalibreliSeri([21.4, 19.8, 25.1, 18.2, 30.0], -0.4);

console.log(hesapSayisi);

let ilkEsikUstu;
for (const deger of seri) {
  if (deger > 24) {
    ilkEsikUstu = deger;
    break;
  }
}

console.log(ilkEsikUstu);
console.log(hesapSayisi);

const hepsi = [...seri];
console.log(hepsi.join(","));
console.log(hesapSayisi);
```

```
0
24.7
3
21,19.4,24.7,17.8,29.6
8
```

Seri kurulduğunda hiçbir hesap yapılmamıştır. Eşiği aşan ilk değer bulunana kadar üç
hesap çalışır; kalan iki değer hiç hesaplanmaz. Aynı seri baştan gezildiğinde beş hesap
daha yapılır ve toplam sekize çıkar.

Aynı iş `map` ile yazılsaydı beş hesap baştan yapılırdı. Programlama Temelleri kursundaki
Fonksiyonel Programlamaya Giriş dersinde tanıtılan **tembel değerlendirme** budur ve
büyük ya da sonu olmayan dizilerde tek uygulanabilir yoldur.

Protokolü elle yazmanın maliyeti de bu örnekte görünür: konum, bitiş koşulu ve kendini
döndüren `[Symbol.iterator]` yöntemi her seferinde tekrarlanır. Sonraki ders bu tekrarı
ortadan kaldıran sözdizimi tanıtacak.

#### Özet

- Yineleme protokolü iki sözleşmeden oluşur: gezilebilir nesne `[Symbol.iterator]` sunar,
  yineleyici `next` sunar.
- `for...of`, yayma, ayrıştırma, `Array.from` ve argüman yayma aynı sözleşmeyi kullanır;
  tek bir yöntem eklemek hepsini çalıştırır.
- Sınıf gövdesine yazılan `[Symbol.iterator]` prototipte durur; gezilebilirlik zincirde
  bulunan bir yöntemdir.
- Yineleyici tükenir, gezilebilir nesne her gezinmede yeni yineleyici üretir; iki kez
  gezilmesi gereken yerde bu ayrım belirleyicidir.
- `break` ve erken ayrıştırma, yineleyicinin `return` yöntemini çağırarak kaynak
  temizliğine olanak verir.
- Değerler istendikçe üretilir; okunmayan değer hiç hesaplanmaz.

#### Sonraki Adım

Elle yazılan yineleyicilerde aynı üç parça sürekli tekrarlandı: konumu tutan değişken,
bitiş koşulu ve kendini döndüren `[Symbol.iterator]` yöntemi. Dil, bu üçünü de kendisi
üreten bir fonksiyon türü sunar — çalışmasını durdurup kaldığı yerden sürdürebilen
fonksiyonlar. Sonraki ders üreteçleri ele alacak ve bu dersteki tembel seriyi birkaç
satıra indirecek.

### Üreteçler

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/uretecler

Duraklatılabilir fonksiyonlar, üreteç nesnesinin yineleme protokolündeki yeri, sonu olmayan diziler, yetki devri, iki yönlü iletişim ve tembel boru hatları.

Önceki derste elle yazılan yineleyicilerde aynı üç parça sürekli tekrarlandı: konumu tutan
değişken, bitiş koşulu ve kendini döndüren `[Symbol.iterator]` yöntemi. Üçü de mekaniktir
ve fonksiyonun asıl işini gölgeler.

**Üreteç (generator)**, yürütmesini durdurup daha sonra kaldığı yerden sürdürebilen bir
fonksiyondur. `function*` biçiminde tanımlanır ve `yield` deyimiyle bir değer verip durur.
Çağrıldığında gövdesi çalışmaz; protokolün iki sözleşmesini de karşılayan bir nesne
döndürür. Ders bu nesnenin davranışını açar ve önceki dersteki tembel seriyi birkaç
satıra indirir.

#### Duraklatılabilir Fonksiyon

Üreteç fonksiyonun gövdesi, ilk `next` çağrısına kadar hiç çalışmaz. Her `next`, bir
sonraki `yield` deyimine kadar ilerler ve orada durur.

```js
let hesapSayisi = 0;

function* kalibreliSeri(hamDegerler, kayma) {
  for (const ham of hamDegerler) {
    hesapSayisi += 1;
    yield Number((ham + kayma).toFixed(2));
  }
}

const uretec = kalibreliSeri([21.4, 19.8, 25.1], -0.4);

console.log(hesapSayisi);
console.log(typeof uretec.next);
console.log(uretec[Symbol.iterator]() === uretec);

console.log(JSON.stringify(uretec.next()));
console.log(hesapSayisi);
console.log(JSON.stringify(uretec.next()));
console.log(JSON.stringify(uretec.next()));
console.log(JSON.stringify(uretec.next()));
console.log(hesapSayisi);

console.log([...kalibreliSeri([21.4, 19.8], -0.4)].join(","));
console.log(typeof kalibreliSeri);
```

```
0
function
true
{"value":21,"done":false}
1
{"value":19.4,"done":false}
{"value":24.7,"done":false}
{"done":true}
3
21,19.4
function
```

İlk satır gövdenin hiç çalışmadığını gösterir. Üçüncü satır üreteç nesnesinin hem
yineleyici hem gezilebilir olduğunu doğrular: kendisini döndüren bir `[Symbol.iterator]`
yöntemi taşır. Önceki derste elle yazılan üç parçanın hepsi burada dilin kendisi
tarafından üretilmiştir.

Beşinci satır tembelliği ölçer: ilk `next` yalnızca bir hesap yapmıştır. Değerler bittiğinde
`done` alanı doğru olur ve `value` alanı `undefined` kalır. Son satır bir ayrımı sabitler —
üreteç fonksiyonun kendisi sıradan bir fonksiyondur; üreteç olan, çağrının döndürdüğü
nesnedir.

Üreteç nesnesi de tükenir. Yeniden gezmek için fonksiyonun tekrar çağrılması gerekir;
onuncu satırdaki yayma bunun için yeni bir üreteç kurar.

#### Sınıf Yöntemi Olarak Üreteç

Yöntem adının önüne yıldız konarak sınıf gövdesinde de üreteç tanımlanabilir. Önceki
dersteki `OlcumSerisi` sınıfı böylece yarı yarıya kısalır.

```js
class OlcumSerisi {
  constructor(sensor, degerler, kayma = 0) {
    this.sensor = sensor;
    this.degerler = degerler;
    this.kayma = kayma;
  }

  *[Symbol.iterator]() {
    for (const ham of this.degerler) {
      yield Number((ham + this.kayma).toFixed(2));
    }
  }

  *etiketliCiftler() {
    let sira = 0;
    for (const deger of this) {
      yield [`${this.sensor}#${sira}`, deger];
      sira += 1;
    }
  }
}

const seri = new OlcumSerisi("S-01", [21.4, 19.8, 25.1], -0.4);

console.log([...seri].join(","));
console.log([...seri].join(","));

for (const [etiket, deger] of seri.etiketliCiftler()) {
  console.log(`${etiket} -> ${deger}`);
}

console.log(Object.getOwnPropertyNames(OlcumSerisi.prototype).join(","));
console.log(Object.hasOwn(OlcumSerisi.prototype, Symbol.iterator));
console.log(new Map(seri.etiketliCiftler()).get("S-01#1"));
```

```
21,19.4,24.7
21,19.4,24.7
S-01#0 -> 21
S-01#1 -> 19.4
S-01#2 -> 24.7
constructor,etiketliCiftler
true
19.4
```

Seri iki kez gezilebilir, çünkü gezilen şey serinin kendisidir ve her gezinme yeni bir
üreteç nesnesi üretir. `etiketliCiftler` yöntemi ise `for (const deger of this)` yazarak
kendi sınıfının gezilebilirliğinden yararlanır; ham veriye ikinci kez erişmez.

Altıncı ve yedinci satırlar zincirle bağı yineler: üreteç yöntemleri de prototipte
durur ve simge anahtarlı olan `Symbol.iterator` ad listesine —
`Object.getOwnPropertyNames` çıktısına — girmez. Son satır ise protokolün
birleştirici gücünü gösterir — anahtar–değer çiftleri veren herhangi bir gezilebilir
nesne doğrudan `Map` yapıcısına verilebilir.

#### Sonu Olmayan Diziler ve Yetki Devri

Üreteç yalnızca istendiğinde çalıştığı için, sonu olmayan bir dizi tanımlamak
sorunsuzdur. `yield*` ise bir üretecin işi başka bir gezilebilir nesneye devretmesini
sağlar.

```js
function* artanZaman(baslangic, adim) {
  let zaman = baslangic;
  while (true) {
    yield zaman;
    zaman += adim;
  }
}

function* ilkN(gezilebilir, adet) {
  if (adet <= 0) return;
  let kalan = adet;
  for (const deger of gezilebilir) {
    yield deger;
    kalan -= 1;
    if (kalan <= 0) return;
  }
}

console.log([...ilkN(artanZaman(1000, 60), 4)].join(","));

function* onSeri() {
  yield 21.4;
  yield 19.8;
}

function* arkaSeri() {
  yield 25.1;
}

function* tumSeri() {
  yield* onSeri();
  yield* arkaSeri();
  yield* [18.2, 30.0];
  return "bitti";
}

const birlesik = tumSeri();
console.log([...tumSeri()].join(","));

let adim = birlesik.next();
const degerler = [];
while (!adim.done) {
  degerler.push(adim.value);
  adim = birlesik.next();
}
console.log(degerler.length);
console.log(adim.value);
```

```
1000,1060,1120,1180
21.4,19.8,25.1,18.2,30
5
bitti
```

`artanZaman` sonsuz bir döngü içerir ama program donmaz: değerler yalnızca istendiğinde
üretilir ve `ilkN` dördüncü değerden sonra durur. Sonsuz döngünün tehlikesiz olması
tembelliğin doğrudan sonucudur; aynı ölçüm zamanlarını bir dizide üretmek imkânsızdır.

`yield*` her tür gezilebilir nesneyi kabul eder — başka bir üreteç ya da sıradan bir dizi.
`return` deyiminin değeri ise `for...of` ve yayma tarafından **atlanır**; yalnızca `next`
çağrısını elle sürdüren kod, bitiş adımının `value` alanında görebilir. Bu yüzden üreteçler
sonuç döndürmek için değil, değer akıtmak için kullanılır.

#### İki Yönlü İletişim

`next` çağrısına verilen argüman, üretecin durduğu `yield` ifadesinin değeri olur. Böylece
üreteç yalnızca değer üretmez, dışarıdan değer de alır.

```js
function* esikDenetleyici(baslangicEsigi) {
  let esik = baslangicEsigi;
  const asanlar = [];
  try {
    while (true) {
      const gelen = yield `esik=${esik}, asan=${asanlar.length}`;
      if (typeof gelen === "number") {
        if (gelen > esik) asanlar.push(gelen);
      } else if (typeof gelen === "object" && gelen !== null) {
        esik = gelen.yeniEsik;
      }
    }
  } finally {
    console.log(`kapanis: toplam asan=${asanlar.length}`);
  }
}

const denetleyici = esikDenetleyici(20);

console.log(denetleyici.next("bu deger yok sayilir").value);
console.log(denetleyici.next(21.4).value);
console.log(denetleyici.next(19.8).value);
console.log(denetleyici.next({ yeniEsik: 25 }).value);
console.log(denetleyici.next(25.1).value);
console.log(JSON.stringify(denetleyici.return("elle durduruldu")));
console.log(JSON.stringify(denetleyici.next(30)));
```

```
esik=20, asan=0
esik=20, asan=1
esik=20, asan=1
esik=25, asan=1
esik=25, asan=2
kapanis: toplam asan=2
{"value":"elle durduruldu","done":true}
{"done":true}
```

İlk `next` çağrısının argümanı yok sayılır: gövde henüz hiçbir `yield` ifadesinde durmuş
değildir, dolayısıyla değeri alacak bir yer yoktur. Sonraki çağrılarda gönderilen değer
`gelen` değişkenine düşer.

`return` yöntemi üreteci dışarıdan sonlandırır ve `finally` bloğunu çalıştırır — önceki
dersteki temizlik noktasının üreteçlerdeki karşılığı budur. Sonlanmış bir üreteç sonraki
`next` çağrılarında bitmiş olarak yanıt verir. `throw` yöntemi de benzer biçimde, hatayı
üretecin durduğu noktaya yükseltir.

Kapanışlar dersindeki sayaç ile karşılaştırıldığında fark açıktır: kapanış durumu tutar
ama yürütmeyi tutmaz. Üreteç, durumun yanında **programın nerede kaldığını** da saklar.

#### Tembel Boru Hattı

Üreteçlerin en somut kazancı, dönüşüm zincirlerinin ara diziler üretmeden kurulmasıdır.
Süzme ve eşleme birer üreteç olarak yazıldığında, her değer boru hattının tamamından tek
tek geçer.

```js
let okumaSayisi = 0;
let donusumSayisi = 0;

function* hamKaynak(degerler) {
  for (const deger of degerler) {
    okumaSayisi += 1;
    yield deger;
  }
}

function* suz(gezilebilir, olcut) {
  for (const deger of gezilebilir) {
    if (olcut(deger)) yield deger;
  }
}

function* esle(gezilebilir, donusum) {
  for (const deger of gezilebilir) {
    donusumSayisi += 1;
    yield donusum(deger);
  }
}

function* ilkN(gezilebilir, adet) {
  if (adet <= 0) return;
  let kalan = adet;
  for (const deger of gezilebilir) {
    yield deger;
    kalan -= 1;
    if (kalan <= 0) return;
  }
}

const ham = [21.4, 19.8, 25.1, 18.2, 30.0, 27.3, 15.0];
const boruHatti = ilkN(
  esle(
    suz(hamKaynak(ham), (d) => d > 20),
    (d) => Number((d * 1.8 + 32).toFixed(1)),
  ),
  2,
);

console.log(okumaSayisi);
console.log([...boruHatti].join(","));
console.log(okumaSayisi);
console.log(donusumSayisi);

const dizilerle = ham
  .filter((d) => d > 20)
  .map((d) => Number((d * 1.8 + 32).toFixed(1)))
  .slice(0, 2);
console.log(dizilerle.join(","));
```

```
0
70.5,77.2
3
2
70.5,77.2
```

İki yol aynı sonucu verir; yaptıkları iş farklıdır. Üreteçli boru hattı yedi ham değerden
yalnızca üçünü okur ve iki dönüşüm yapar; gerekli olan iki sonuç elde edildiği anda durur.
Dizi yöntemleriyle yazılan sürüm önce beş değeri süzer, beşini de dönüştürür, sonra ilk
ikisini alır — üstelik iki ara dizi üretir.

Fark, veri kaynağı büyüdükçe ya da dönüşüm pahalılaştıkça belirginleşir. Buna karşılık
üreteçli kuruluşun sabit bir maliyeti vardır: her değer için birkaç yürütme geçişi olur.
Küçük dizilerde dizi yöntemleri hem daha kısa hem daha hızlıdır; ölçüt, işlenmeyen
elemanların sayısıdır.

#### Özet

- Üreteç fonksiyon çağrıldığında gövdesi çalışmaz; hem yineleyici hem gezilebilir olan bir
  üreteç nesnesi döner.
- Her `next` çağrısı bir sonraki `yield` deyimine kadar ilerler; yürütme orada durur ve
  durum korunur.
- Sınıf gövdesinde yıldızlı yöntem tanımlanabilir; üreteç yöntemleri de prototipte durur.
- `yield*` işi başka bir gezilebilir nesneye devreder; `return` deyiminin değeri yalnızca
  elle sürdürülen `next` çağrılarında görünür.
- `next` argümanı, üretecin durduğu `yield` ifadesinin değeri olur; `return` yöntemi
  `finally` bloğunu çalıştırarak temizlik yapar.
- Üreteçlerden kurulu boru hattı ara dizi üretmez ve yalnızca istenen kadar değer işler.

#### Sonraki Adım

Bu konudaki koleksiyonların hepsi keyfi JavaScript değerleri tuttu: sayı, dizgi, nesne.
Bir ölçüm akışını dosyaya yazmak ya da ağ üzerinden aktarmak gerektiğinde ise veri, bayt
dizisi olarak temsil edilmelidir. Sonraki ders sabit boyutlu ikili arabellekleri ve
üzerlerindeki tipli görünümleri ele alacak; bayt sırası ve taşma davranışı da orada
sınanacak.

### Tipli Diziler

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/tipli-diziler

Sabit boyutlu ikili arabellekler ve üzerlerindeki tipli görünümler, taşma ile kırpma davranışı, bayt sırasının açıkça belirtilmesi ve tipli dizilerin sıradan dizilerden farkları.

Bu konudaki koleksiyonların hepsi keyfi JavaScript değerleri tuttu. Bir ölçüm akışını
dosyaya yazmak ya da ağ üzerinden aktarmak gerektiğinde ise veri, belirli sayıda bayta
oturan sabit bir düzende temsil edilmelidir.

**Tipli diziler (typed arrays)** bu iş için vardır. İkisi bir arada çalışır: ham baytları
tutan `ArrayBuffer` ve o baytları belirli bir sayı türü olarak yorumlayan **görünüm**.
Bilgisayarlar Nasıl Çalışır kursundaki İşaretli Tam Sayılar, Kayan Noktalı Sayılar ve
Bayt Sırası derslerinde kurulan temsiller, burada doğrudan gözlemlenebilir hâle gelir.

#### Arabellek ve Görünüm

`ArrayBuffer` yalnızca bayt tutar; içeriğine doğrudan erişilmez. Erişim için üzerine bir
görünüm kurulur ve görünümün türü, aynı baytların nasıl okunacağını belirler.

```js
const arabellek = new ArrayBuffer(16);

console.log(arabellek.byteLength);

const baytGorunumu = new Uint8Array(arabellek);
const ondalikGorunum = new Float64Array(arabellek);

console.log(baytGorunumu.length);
console.log(ondalikGorunum.length);
console.log(Uint8Array.BYTES_PER_ELEMENT);
console.log(Float64Array.BYTES_PER_ELEMENT);

ondalikGorunum[0] = 21.4;
console.log(ondalikGorunum[0]);
console.log([...baytGorunumu.slice(0, 8)].join(","));

baytGorunumu[0] = 0;
console.log(ondalikGorunum[0]);
console.log(baytGorunumu.buffer === ondalikGorunum.buffer);

const bagimsiz = new Float64Array(2);
console.log(bagimsiz.buffer === arabellek);
console.log(bagimsiz[0]);
```

```
16
16
2
1
8
21.4
102,102,102,102,102,102,53,64
21.399999999999636
true
false
0
```

Aynı on altı bayt, bir görünümde on altı tam sayı, diğerinde iki ondalık sayıdır. İki
görünüm de aynı belleğe bakar: ondalık görünüme yazılan değerin baytları bayt görünümünde
okunur ve bayt görünümünde yapılan tek bir değişiklik ondalık değeri bozar. Sekizinci
satırdaki sapma, en düşük anlamlı baytın sıfırlanmasının kayan noktalı gösterimdeki
karşılığıdır.

Bayt dizilişi platformun **yerel bayt sırasına** bağlıdır; yukarıdaki çıktı küçük uçlu bir
makinede üretilmiştir. Büyük uçlu bir makinede aynı program farklı bir dizilim gösterir.
Taşınabilirliğin nasıl sağlanacağı bu dersin üçüncü bölümünün konusudur.

Bir arabellek verilmeden kurulan görünüm, kendi arabelleğini yaratır ve sıfırlarla
başlar.

#### Taşma ve Kırpma

Tipli dizinin bir gözü, türünün izin verdiği aralığın dışında bir değer alamaz. Aralık
dışı değerlerin ne olacağı türe göre değişir.

```js
const sarma = new Uint8Array(4);
const kirpma = new Uint8ClampedArray(4);
const isaretli = new Int8Array(4);

const denemeler = [300, -1, 255.9, 128];

denemeler.forEach((deger, sira) => {
  sarma[sira] = deger;
  kirpma[sira] = deger;
  isaretli[sira] = deger;
});

console.log([...sarma].join(","));
console.log([...kirpma].join(","));
console.log([...isaretli].join(","));

const tamsayi = new Int32Array(1);
tamsayi[0] = 21.9;
console.log(tamsayi[0]);

const ondalik = new Float32Array(1);
ondalik[0] = 21.4;
console.log(ondalik[0]);
console.log(ondalik[0] === 21.4);

const cifteDuyarlik = new Float64Array(1);
cifteDuyarlik[0] = 21.4;
console.log(cifteDuyarlik[0] === 21.4);
```

```
44,255,255,128
255,0,255,128
44,-1,-1,-128
21
21.399999618530273
false
true
```

`Uint8Array` değeri 256'ya göre sarar: 300 değeri 44 olur, -1 değeri 255. `Uint8ClampedArray`
ise aralığa **kırpar**: 300 değeri 255'te, -1 değeri 0'da durur. `Int8Array` aynı baytları
işaretli yorumlar; 128 değeri -128 olarak okunur — ikiye tümleyen gösteriminin doğrudan
sonucudur.

Tam sayı görünümlerinde ondalık kısım atılır, yuvarlama yapılmaz: 21.9 değeri 21 olur.
Kırpmalı türde ise en yakın tam sayıya yuvarlanır; 255.9 değeri 256 olur ve aralığa
kırpılarak 255'te kalır.

Son üç satır duyarlık farkını gösterir. `Float32Array` sayıyı tek duyarlıkta saklar ve
geri okunan değer aslından farklıdır; `Float64Array` ise JavaScript sayılarının kendi
gösterimini kullandığı için değeri aynen korur. İkili kayıt biçimlerinde alan genişliği
seçimi bu ödünleşimi doğrudan belirler.

#### Bayt Sırasını Açıkça Belirtmek

Tipli dizi görünümleri yerel bayt sırasını kullanır; bu, verinin başka bir makinede
okunması gerektiğinde sorun çıkarır. `DataView`, her okuma ve yazmada bayt sırasının
açıkça belirtilmesini sağlar.

Ölçüm kayıtlarını on iki baytlık sabit bir düzene yerleştirelim: dört bayt sensör numarası,
sekiz bayt ölçüm değeri.

```js
const KAYIT_BOYU = 12;

function kayitlariYaz(kayitlar, kucukUclu) {
  const arabellek = new ArrayBuffer(KAYIT_BOYU * kayitlar.length);
  const gorunum = new DataView(arabellek);
  kayitlar.forEach((kayit, sira) => {
    const ofset = sira * KAYIT_BOYU;
    gorunum.setUint32(ofset, kayit.sensorNo, kucukUclu);
    gorunum.setFloat64(ofset + 4, kayit.deger, kucukUclu);
  });
  return arabellek;
}

function kayitlariOku(arabellek, kucukUclu) {
  const gorunum = new DataView(arabellek);
  const sonuc = [];
  for (let ofset = 0; ofset < arabellek.byteLength; ofset += KAYIT_BOYU) {
    sonuc.push({
      sensorNo: gorunum.getUint32(ofset, kucukUclu),
      deger: gorunum.getFloat64(ofset + 4, kucukUclu),
    });
  }
  return sonuc;
}

const kayitlar = [
  { sensorNo: 1, deger: 21.4 },
  { sensorNo: 2, deger: 19.8 },
];

const kucukUcluArabellek = kayitlariYaz(kayitlar, true);
const buyukUcluArabellek = kayitlariYaz(kayitlar, false);

console.log(kucukUcluArabellek.byteLength);
console.log([...new Uint8Array(kucukUcluArabellek).slice(0, 4)].join(","));
console.log([...new Uint8Array(buyukUcluArabellek).slice(0, 4)].join(","));

console.log(JSON.stringify(kayitlariOku(kucukUcluArabellek, true)));
console.log(JSON.stringify(kayitlariOku(buyukUcluArabellek, false)));
console.log(kayitlariOku(buyukUcluArabellek, true)[0].sensorNo);
```

```
24
1,0,0,0
0,0,0,1
[{"sensorNo":1,"deger":21.4},{"sensorNo":2,"deger":19.8}]
[{"sensorNo":1,"deger":21.4},{"sensorNo":2,"deger":19.8}]
16777216
```

İki arabellek aynı kayıtları taşır ama baytları ters sırada dizilidir. Bu çıktı platformdan
bağımsızdır: `DataView` yazımda ve okumada sırayı açıkça aldığı için makinenin yerel
sırasına bakmaz.

Son satır yanlış varsayımın bedelini gösterir. Büyük uçlu yazılmış veri küçük uçlu
okunduğunda `1` yerine `16777216` çıkar; hata yükselmez, yalnızca sayı anlamsızlaşır.
Bilgisayar Ağları müfredatındaki ağ bayt sırası tartışması aynı sorunun protokol
düzeyindeki karşılığıdır: ikili bir biçim tanımlanırken bayt sırası biçimin parçası olarak
yazılmalıdır.

#### Sıradan Diziden Farkları

Tipli diziler `Array` türünün bir alt türü değildir. Prototip zincirleri ayrıdır ve dizi
davranışlarının bir kısmını taşımazlar.

```js
const tipli = new Float64Array([21.4, 19.8, 25.1]);
const siradan = [21.4, 19.8, 25.1];

console.log(Array.isArray(tipli));
console.log(Array.isArray(siradan));
console.log(typeof tipli.push);
console.log(typeof siradan.push);

tipli[5] = 99;
console.log(tipli.length);
console.log(tipli[5]);

const ustPrototip = Object.getPrototypeOf(Float64Array.prototype);
console.log(ustPrototip === Object.getPrototypeOf(Uint8Array.prototype));
console.log(ustPrototip === Array.prototype);
console.log(Object.getPrototypeOf(ustPrototip) === Object.prototype);

const esleme = tipli.map((d) => d * 2);
console.log(esleme.constructor === Float64Array);
console.log([...esleme].join(","));
console.log([...tipli].join(","));
console.log(JSON.stringify(tipli));
console.log(JSON.stringify(Array.from(tipli)));
```

```
false
true
undefined
function
3
undefined
true
false
true
true
42.8,39.6,50.2
21.4,19.8,25.1
{"0":21.4,"1":19.8,"2":25.1}
[21.4,19.8,25.1]
```

Uzunluk sabittir: sınır dışına yazma sessizce yok sayılır, `push` gibi büyüten yöntemler
bulunmaz. Bu, Veri Yapıları kursundaki Diziler ile Dinamik Diziler ayrımının dildeki
karşılığıdır — tipli dizi birincisi, sıradan dizi ikincisidir.

Zincir yapısı da ayrıdır. Tüm tipli dizi türleri ortak bir ara prototipi paylaşır; `map`,
`filter`, `reduce` gibi yöntemler orada tanımlıdır ve `Array.prototype` ile hiçbir bağları
yoktur. Ara prototipin prototipi doğrudan `Object.prototype` nesnesidir. `map` çağrısının
yine tipli dizi döndürmesi de bu ayrı gerçekleştirimin sonucudur.

Dizileştirmede fark belirgindir: `JSON.stringify` tipli diziyi sayısal anahtarlı bir nesne
olarak yazar, dizi olarak değil. İkili veriyi metin biçiminde taşımak gerekiyorsa önce
sıradan diziye çevrilmelidir.

#### Görünüm mü, Kopya mı

Aynı arabellek üzerinde çalışan iki yöntem, bu kursun son dersine doğrudan bağlanan bir
ayrım kurar: `subarray` bir görünüm, `slice` bir kopya döndürür.

```js
const tumOlcumler = new Float64Array([21.4, 19.8, 25.1, 18.2, 30.0]);

const pencereGorunumu = tumOlcumler.subarray(1, 4);
const pencereKopyasi = tumOlcumler.slice(1, 4);

console.log([...pencereGorunumu].join(","));
console.log([...pencereKopyasi].join(","));
console.log(pencereGorunumu.buffer === tumOlcumler.buffer);
console.log(pencereKopyasi.buffer === tumOlcumler.buffer);

pencereGorunumu[0] = 0;
pencereKopyasi[1] = 0;

console.log([...tumOlcumler].join(","));
console.log([...pencereKopyasi].join(","));

console.log(pencereGorunumu.byteOffset);
console.log(pencereKopyasi.byteOffset);
console.log(tumOlcumler.byteLength);
```

```
19.8,25.1,18.2
19.8,25.1,18.2
true
false
21.4,0,25.1,18.2,30
19.8,0,18.2
8
0
40
```

İki pencere başlangıçta aynı değerleri gösterir. Görünüme yazmak asıl diziyi değiştirir,
kopyaya yazmak değiştirmez. Bellek açısından da farklıdırlar: görünüm yeni bayt ayırmaz ve
asıl arabellek içinde bir ofsetten başlar; kopya kendi arabelleğini yaratır.

Büyük ölçüm bloklarında pencere işlemleri için görünüm kullanmak, veriyi çoğaltmadan
çalışmayı sağlar. Buna karşılık görünüm asıl arabelleği erişilebilir tuttuğu için, küçük
bir pencere büyük bir arabelleği bellekte yaşatabilir — Zayıf Koleksiyonlar dersindeki
erişilebilirlik tartışmasının bir başka biçimi.

#### Özet

- `ArrayBuffer` ham baytları tutar; erişim, baytları belirli bir sayı türü olarak
  yorumlayan görünümlerle yapılır.
- Aynı arabellek üzerinde birden çok görünüm kurulabilir ve hepsi aynı belleği paylaşır.
- Tam sayı görünümleri aralık dışı değerleri sarar, kırpmalı tür ise aralığa kırpar;
  ondalık kısım tam sayı türlerinde atılır.
- Tipli dizi görünümleri yerel bayt sırasını kullanır; taşınabilir ikili biçimler için
  `DataView` ile sıra açıkça belirtilir.
- Tipli diziler `Array` türünden ayrı bir zincire sahiptir: uzunlukları sabittir,
  büyütülemezler ve dizileştirmede nesne olarak yazılırlar.
- `subarray` aynı belleğe bakan bir görünüm, `slice` bağımsız bir kopya döndürür.

#### Sonraki Adım

Bu derste kopya ile görünüm ayrımı bayt düzeyinde göründü; aynı soru nesneler için kursun
başından beri açıktı. Nesne yayma zinciri kaybetti, `Object.assign` erişimcileri düz
değere çevirdi, `JSON.stringify` yöntemleri ve `Map` içeriğini attı. Son ders bu ayrımı
toplu olarak ele alacak: bir nesneyi kopyalarken neyin taşındığını, neyin düştüğünü ve
derin kopyanın hangi yollarla — hangi kayıplarla — yapıldığını gösterecek.

### Yapısal Kopyalama

Source: https://academia.sh/tr/kurslar/javascript-nesne-modeli/yapisal-kopyalama

Sığ kopyanın paylaştığı başvurular, dizileştirme yoluyla kopyalamanın kayıpları, yapısal klonlamanın kapsamı ve prototip ile tanımlayıcıları koruyan derin kopya.

Önceki derste kopya ile görünüm ayrımı bayt düzeyinde göründü. Aynı soru nesneler için
kursun başından beri açıktı: nesne yayma zinciri kaybetti, `Object.assign` erişimcileri
düz değere çevirdi, `JSON.stringify` yöntemleri ve `Map` içeriğini attı.

Bu ders o soruyu toplu olarak yanıtlar. Üç kopyalama yolu — yayma, dizileştirme yoluyla
gidiş dönüş ve yapısal klonlama — ayrı ayrı incelenir; her birinin neyi taşıdığı ve neyi
düşürdüğü gösterilir. Ardından her ikisini de koruyan bir derin kopya yazılır.

#### Sığ Kopyanın Sınırı

Nesne yayma ve `Object.assign`, kaynağın kendi sayılabilir özelliklerini hedefe yazar.
Değer bir nesne ise yazılan şey o nesnenin **başvurusudur**; nesnenin kendisi kopyalanmaz.

```js
const kaynak = {
  sensor: "S-01",
  deger: 21.4,
  kaynakBilgisi: { dosya: "olcum-2.csv", satir: 17 },
  etiketler: ["ham", "sicaklik"],
};

const yayilmis = { ...kaynak };
const atanmis = Object.assign({}, kaynak);

console.log(yayilmis.sensor);
console.log(yayilmis.kaynakBilgisi === kaynak.kaynakBilgisi);
console.log(atanmis.etiketler === kaynak.etiketler);

yayilmis.sensor = "S-99";
yayilmis.kaynakBilgisi.satir = 99;
yayilmis.etiketler.push("degistirildi");

console.log(kaynak.sensor);
console.log(kaynak.kaynakBilgisi.satir);
console.log(kaynak.etiketler.join(","));
console.log(JSON.stringify(kaynak.kaynakBilgisi) === JSON.stringify(yayilmis.kaynakBilgisi));
```

```
S-01
true
true
S-01
99
ham,sicaklik,degistirildi
true
```

Üst düzeydeki alan bağımsızdır: kopyada `sensor` değiştiğinde kaynak etkilenmez. İç
nesneler ise paylaşılır; kopya üzerinden yapılan her değişiklik kaynakta da görünür.
Programlama Temelleri kursundaki Değer ve Referans ile Aktarım dersinde tanıtılan sığ
kopya–derin kopya ayrımı budur.

Değişmezlik Teknikleri dersindeki dondurmanın neden sığ kaldığı da aynı nedendir: her iki
işlem de nesnenin kendi özellikleriyle sınırlıdır, gösterilen nesnelere inmez.

#### Dizileştirme Yoluyla Kopyalama

Bir nesneyi metne çevirip geri okumak, tüm iç nesneleri yeniden yaratır. Kayıpları
büyüktür ve bu kayıplar sessizdir.

```js
const kaynak = {
  sensor: "S-01",
  deger: 21.4,
  kayma: undefined,
  olcumAni: new Date(0),
  etiketKumesi: new Set(["ham", "sicaklik"]),
  kalibrasyon: new Map([["S-01", -0.4]]),
  bicimle() {
    return this.sensor;
  },
};

const jsonKopya = JSON.parse(JSON.stringify(kaynak));

console.log(Object.keys(jsonKopya).join(","));
console.log(typeof jsonKopya.bicimle);
console.log("kayma" in jsonKopya);
console.log(typeof jsonKopya.olcumAni);
console.log(jsonKopya.olcumAni);
console.log(JSON.stringify(jsonKopya.etiketKumesi));
console.log(JSON.stringify(jsonKopya.kalibrasyon));

const cevrimli = { sensor: "S-02" };
cevrimli.kendisi = cevrimli;

try {
  JSON.stringify(cevrimli);
} catch (hata) {
  console.log(`cevrim: ${hata.constructor.name}`);
}
```

```
sensor,deger,olcumAni,etiketKumesi,kalibrasyon
undefined
false
string
1970-01-01T00:00:00.000Z
{}
{}
cevrim: TypeError
```

Beş kayıp sıralanabilir. Fonksiyonlar ve `undefined` değerli alanlar tümüyle düşer.
Tarih nesnesi dizgiye dönüşür ve geri okunduğunda tarih değil metin olur. `Set` ve `Map`
içerikleri boş nesneye iner — İleri Koleksiyonlar konusunun ilk dersinde görülen davranış.
Sayılamaz özellikler ve simge anahtarlar da atlanır. Son olarak çevrimsel başvurular hata
yükseltir.

Prototip zinciri de kaybolur: geri okunan nesne her zaman sıradan bir nesnedir. Bu yol
yalnızca veri taşıyan, iç içe geçmiş sade nesneler için uygundur — zaten JSON biçiminin
kapsamı da budur.

#### Yapısal Klonlama

Çalışma zamanının sunduğu `structuredClone` işlevi, dizileştirmeden çok daha geniş bir
değer kümesini derin olarak kopyalar ve çevrimleri korur.

```js
class OlcumKaydi {
  constructor(sensor, deger) {
    this.sensor = sensor;
    this.deger = deger;
    this.etiketKumesi = new Set(["ham"]);
    this.olcumAni = new Date(0);
  }
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  }
}

const kayit = new OlcumKaydi("S-01", 21.4);
kayit.kendisi = kayit;

const kopya = structuredClone(kayit);

console.log(kopya.sensor);
console.log(kopya.kendisi === kopya);
console.log(kopya.etiketKumesi instanceof Set);
console.log(kopya.etiketKumesi.has("ham"));
console.log(kopya.olcumAni instanceof Date);
console.log(kopya.olcumAni.getTime());
console.log(kopya.etiketKumesi === kayit.etiketKumesi);

console.log(Object.getPrototypeOf(kopya) === OlcumKaydi.prototype);
console.log(kopya instanceof OlcumKaydi);
console.log(typeof kopya.bicimle);

try {
  structuredClone({ islev: () => 1 });
} catch (hata) {
  console.log(`islev: ${hata.name}`);
}

const tipli = new Float64Array([21.4, 19.8]);
const tipliKopya = structuredClone(tipli);
console.log(tipliKopya instanceof Float64Array);
console.log(tipliKopya.buffer === tipli.buffer);
```

```
S-01
true
true
true
true
0
false
false
false
undefined
islev: DataCloneError
true
false
```

Kazanımlar açıktır: çevrimsel başvuru korunur — kopyanın kendine dönen alanı kopyayı
gösterir, kaynağı değil. `Set`, `Date` ve tipli diziler türleriyle birlikte
kopyalanır ve iç nesneler paylaşılmaz.

Kayıp tektir ama bu kursun ekseni açısından belirleyicidir: **prototip zinciri taşınmaz.**
Sekizinci, dokuzuncu ve onuncu satırlar bunu üç ayrı biçimde gösterir — kopyanın prototipi
sınıfın prototipi değildir, tür sorgusu olumsuzdur ve yöntem bulunamaz. Sınıf örneği
kopyalandığında geriye yalnızca veri kalır.

Fonksiyonlar da kopyalanamaz ve hata yükseltir. Bu iki sınır aynı gerekçeden gelir:
yapısal klonlama, değerleri başka bir yürütme bağlamına aktarabilmek için tanımlanmıştır;
kod ve zincir aktarılamayacak şeylerdir.

#### Zinciri ve Tanımlayıcıları Koruyan Kopya

Hem derinlik hem zincir gerekiyorsa kopya elle yazılmalıdır. Kalıp, Değişmezlik Teknikleri
dersindeki derin dondurmanın kardeşidir: özyinelemeli gezinti, çevrimler için ziyaret
tablosu ve her düğümde prototipin yeniden kurulması.

```js
function derinKopyala(deger, gorulen = new WeakMap()) {
  if (deger === null || typeof deger !== "object") return deger;
  if (gorulen.has(deger)) return gorulen.get(deger);

  if (deger instanceof Date) return new Date(deger.getTime());
  if (deger instanceof Set) {
    const kume = new Set();
    gorulen.set(deger, kume);
    for (const oge of deger) kume.add(derinKopyala(oge, gorulen));
    return kume;
  }
  if (deger instanceof Map) {
    const harita = new Map();
    gorulen.set(deger, harita);
    for (const [anahtar, icerik] of deger) {
      harita.set(derinKopyala(anahtar, gorulen), derinKopyala(icerik, gorulen));
    }
    return harita;
  }

  const kopya = Array.isArray(deger)
    ? []
    : Object.create(Object.getPrototypeOf(deger));
  gorulen.set(deger, kopya);

  for (const ad of Reflect.ownKeys(deger)) {
    const tanim = Object.getOwnPropertyDescriptor(deger, ad);
    if ("value" in tanim) tanim.value = derinKopyala(tanim.value, gorulen);
    Object.defineProperty(kopya, ad, tanim);
  }
  return kopya;
}

class OlcumKaydi {
  constructor(sensor, deger) {
    this.sensor = sensor;
    this.deger = deger;
    this.kaynakBilgisi = { dosya: "olcum-2.csv", satir: 17 };
  }
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  }
}

const kayit = new OlcumKaydi("S-01", 21.4);
kayit.kendisi = kayit;
Object.defineProperty(kayit, "gizliSayac", {
  value: 3,
  enumerable: false,
  writable: true,
  configurable: true,
});

const kopya = derinKopyala(kayit);

console.log(kopya.bicimle());
console.log(kopya instanceof OlcumKaydi);
console.log(kopya.kendisi === kopya);
console.log(kopya.kaynakBilgisi === kayit.kaynakBilgisi);
console.log(kopya.kaynakBilgisi.satir);
console.log(kopya.gizliSayac);
console.log(Object.keys(kopya).join(","));
console.log(Object.getOwnPropertyDescriptor(kopya, "gizliSayac").enumerable);

kopya.kaynakBilgisi.satir = 99;
console.log(kayit.kaynakBilgisi.satir);
```

```
S-01: 21.4
true
true
false
17
3
sensor,deger,kaynakBilgisi,kendisi
false
17
```

Kopya gerçek bir ölçüm kaydıdır: yöntemi çalışır, tür sorgusu olumludur, çevrimi kendine
döner, iç nesnesi bağımsızdır ve sayılamaz alanı bayrağıyla birlikte korunmuştur.
`Reflect.ownKeys` seçimi, simge anahtarların da kapsanmasını sağlar; tanımlayıcının
kopyalanması ise erişimcilerin düz değere düşmesini engeller.

Bu kalıbın sınırları da vardır. Özel alanlar kopyalanamaz — dışarıdan okunamadıkları için
bir kütüphane işlevinin erişimi yoktur. Kapanışta tutulan durum da aynı nedenle taşınmaz.
Genel kural şudur: **kopyalanabilen şey, dışarıdan görülebilen şeydir.**

#### Üç Kuruluş Kopyalama Altında

Kurs boyunca ölçüm kaydı üç biçimde kuruldu. Kopyalama, aralarındaki farkı en keskin
biçimde ortaya koyan sınamadır.

```js
function duzKayit(sensor, deger) {
  return {
    sensor,
    deger,
    bicimle() {
      return `${this.sensor}: ${this.deger}`;
    },
  };
}

function kapanisliKayit(sensor, deger) {
  return {
    bicimle() {
      return `${sensor}: ${deger}`;
    },
  };
}

class SinifKayit {
  constructor(sensor, deger) {
    this.sensor = sensor;
    this.deger = deger;
  }
  bicimle() {
    return `${this.sensor}: ${this.deger}`;
  }
}

function denetle(ad, nesne) {
  const yayilmis = { ...nesne };
  let klonSonucu;
  try {
    const klon = structuredClone(nesne);
    klonSonucu = typeof klon.bicimle === "function" ? klon.bicimle() : "davranis yok";
  } catch (hata) {
    klonSonucu = `hata:${hata.name}`;
  }
  const yayilmaSonucu =
    typeof yayilmis.bicimle === "function" ? yayilmis.bicimle() : "davranis yok";
  console.log(`${ad}: json=${JSON.stringify(nesne)}, yayma=${yayilmaSonucu}, klon=${klonSonucu}`);
}

denetle("duz   ", duzKayit("S-01", 21.4));
denetle("kapanis", kapanisliKayit("S-01", 21.4));
denetle("sinif ", new SinifKayit("S-01", 21.4));

const sinifOrnegi = new SinifKayit("S-01", 21.4);
const zincirliKopya = Object.defineProperties(
  Object.create(Object.getPrototypeOf(sinifOrnegi)),
  Object.getOwnPropertyDescriptors(sinifOrnegi),
);
console.log(zincirliKopya.bicimle());
console.log(zincirliKopya instanceof SinifKayit);
```

```
duz   : json={"sensor":"S-01","deger":21.4}, yayma=S-01: 21.4, klon=hata:DataCloneError
kapanis: json={}, yayma=S-01: 21.4, klon=hata:DataCloneError
sinif : json={"sensor":"S-01","deger":21.4}, yayma=davranis yok, klon=davranis yok
S-01: 21.4
true
```

Üç kuruluş üç ayrı davranış gösterir. Düz nesnede veri ve davranış aynı nesnede durduğu
için dizileştirme veriyi alır, yayma davranışı da taşır, yapısal klonlama ise fonksiyon
yüzünden hata verir. Kapanışlı kuruluşta veri hiç özellik olmadığı için dizileştirme boş
çıkar; yayma yalnızca fonksiyonu kopyalar ve fonksiyon kendi kapanışını taşıdığı için
sonuç yine doğrudur.

Sınıf kuruluşunda ise veri ile davranış ayrı yerlerdedir: veri örnekte, davranış zincirde.
Her iki kopyalama yolu da yalnızca örneği aldığı için davranış düşer — ama hata da vermez.
Zincir gerekiyorsa son iki satırdaki gibi açıkça kurulmalıdır.

Kursun ilk dersindeki cümle burada kapanır: **bir nesnenin verisi kendisinde, kimliği
zincirindedir.** Kopyalama işlemleri kendi özellikleri taşır; zincir ancak açıkça
kurulduğunda taşınır.

#### Özet

- Sığ kopya yalnızca üst düzey alanları ayırır; iç nesneler kaynakla paylaşılır.
- Dizileştirme yoluyla kopyalama fonksiyonları, `undefined` alanları, sayılamaz ve simge
  anahtarlı özellikleri düşürür; `Set`, `Map` ve `Date` türlerini bozar, çevrimlerde hata
  verir.
- `structuredClone` çevrimleri korur ve pek çok yerleşik türü türüyle birlikte kopyalar,
  ancak prototip zincirini taşımaz ve fonksiyonlarda hata yükseltir.
- Zinciri ve tanımlayıcıları koruyan kopya elle yazılır: her düğümde prototip yeniden
  kurulur, tanımlayıcılar aynen aktarılır, çevrimler ziyaret tablosuyla izlenir.
- Özel alanlar ve kapanıştaki durum kopyalanamaz; kopyalanabilen şey dışarıdan görülebilen
  şeydir.

#### Kurs Kapanışı

Kurs tek bir soruyla başladı: hiç yazılmamış bir özellik nasıl bulunuyor? Yanıt, her
nesnenin taşıdığı prototip bağıydı ve bu bağ on yedi ders boyunca ölçüldü. Özellik araması
nesnenin kendisinden başlayıp zincirde ilerledi; yazma işlemi hiç yukarı çıkmadı, hep
gölgeledi. `new` işlecinin yaptığı iş üç satırla yeniden yazıldı. Sınıf sözdiziminin her
satırının hangi prototip işlemine karşılık geldiği aynı sınamalarla gösterildi — sınıf,
zincirin sözdizimsel yüzüdür; altında farklı bir nesne modeli yoktur.

İkinci konu, zincirin yanıtlamadığı soruyu ele aldı. Prototip bir fonksiyonun **nerede
bulunacağını** belirler; `this` ise **hangi veri üzerinde çalışacağını**. İkisi bağımsız
olduğu için yöntem nesnesinden koparılabilir, başka bir nesneye ödünç verilebilir ya da
kalıcı biçimde bağlanabilir. Ok fonksiyonları bu dört kuralın dışında kalır ve bağlamı
tanımlandıkları kapsamdan alır. Kapanışlar ise durumu zincire hiç dokunmadan gizleyen
ikinci bir kuruluş yolu sundu.

Üçüncü konu koleksiyonları aynı ölçütlerle inceledi: `Map` anahtar kimliğini korur,
zayıf koleksiyonlar nesnelerin ömrünü uzatmaz, yineleme protokolü zincirde bulunan tek bir
yöntemdir, üreteçler o yöntemi kendileri üretir, tipli diziler ayrı bir zincire sahiptir.
Son ders kopyalama sınamasını kurdu: tür taşıyan yapılar türleriyle birlikte kopyalanır,
ama prototip zinciri taşınmaz.

Ölçüm kaydının üç kuruluşu — düz nesne, fabrika ile kapanış, sınıf — kurs boyunca yan yana
tutuldu ve her yeni kavramda karşılaştırıldı: paylaşım, görünürlük, bağlanma, gizlilik,
dizileştirme ve kopyalama. Aralarında doğru bir seçim yoktur; ölçütler açıktır ve
karşılaştırmalar bu derslerdeki çıktılarla ölçülebilir.

Bir şey eksik kaldı. Bu kurstaki her örnek anında sonuçlandı: her `console.log` çağrısı,
kendinden önceki satır bittikten sonra çalıştı. Ölçümler bir dosyadan ya da ağdan geldiğinde
bu varsayım düşer — sonuç, istendiği anda hazır olmaz. **Eşzamansız JavaScript ve Çalışma
Zamanı** kursu bu durumu ele alır: tek iş parçacıklı yürütme modelini, olay döngüsünün
görev ve mikro görev kuyruklarını, söz tabanlı akışları ve bellek yaşam döngüsünü. Bu
kursta öğrendiğiniz zincir ve bağlam kuralları orada da geçerlidir; değişen tek şey,
kodun **ne zaman** çalıştığıdır.

