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.
WeakMapveWeakSetyalnı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.
WeakRefveFinalizationRegistryzamanlaması 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.