İçeriğe geç
academia.sh

Ders 13 / 17

Zayıf 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.

İçindekiler

Ö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 olabilir, eleman sayısı okunamaz ve içeriği gezilemez.

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. İlkel bir değer anahtar olarak verildiğinde hata yükselir, çünkü ilkel 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.

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.

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.

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.

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 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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat