Axlat yig'ish boâlimidan maâlumki, JavaScript mexanizmi qiymatni xotirada âyetib boriladiganâ boâlib qolgan va potentsial ravishda ishlatilishi mumkin boâlgan vaqt davomida saqlaydi.
Misol uchun:
let john = { name: "John" };
// objektga kirish mumkin, john unga havola
// havolani qayta yozish
john = null;
// objekt xotiradan olib tashlanadi
Odatda, objektning xususiyatlari yoki massiv elementlari yoki boshqa maâlumotlar strukturasi elementlari yetib boriladigan deb hisoblanadi va shu maâlumotlar strukturasi xotirada boâlgancha xotirada saqlanadi.
Misol uchun, agar biz objektni massivga qoâysak, massiv tirik boâlgancha, objekt ham tirik boâladi, hatto unga boshqa havolalar boâlmasa ham.
Mana bunday:
let john = { name: "John" };
let array = [ john ];
john = null; // havolani qayta yozish
// avval john tomonidan havola qilingan objekt massiv ichida saqlanadi
// shuning uchun u garbage-collection qilinmaydi
// biz uni array[0] sifatida olishimiz mumkin
Xuddi shunday, agar biz objektni oddiy Map da kalit sifatida ishlatsak, u holda Map mavjud boâlgancha, bu objekt ham mavjud boâladi. U xotirani egallaydi va garbage collection qilinmasligi mumkin.
Misol uchun:
let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // havolani qayta yozish
// john xarita ichida saqlanadi,
// biz uni map.keys() dan foydalanib olishimiz mumkin
WeakMap bu jihatdan tubdan farq qiladi. U kalit objektlarning garbage-collection qilinishiga toâsqinlik qilmaydi.
Misollar orqali bu nimani anglatishini koâraylik.
WeakMap
Map va WeakMap oârtasidagi birinchi farq shundaki, kalitlar objektlar boâlishi kerak, primitiv qiymatlar emas:
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "ok"); // yaxshi ishlaydi (objekt kaliti)
// satrni kalit sifatida ishlatib bo'lmaydi
weakMap.set("test", "Whoops"); // Xato, chunki "test" objekt emas
Endi, agar biz objektni unda kalit sifatida ishlatsak va bu objektga boshqa havolalar boâlmasa â u xotiradan (va xaritadan) avtomatik ravishda olib tashlanadi.
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // havolani qayta yozish
// john xotiradan olib tashlandi!
Uni yuqoridagi oddiy Map misoli bilan solishtiring. Endi agar john faqat WeakMap ning kaliti sifatida mavjud boâlsa â u xaritadan (va xotiradan) avtomatik ravishda oâchiriladi.
WeakMap iteratsiyani va keys(), values(), entries() metodlarini qoâllab-quvvatlamaydi, shuning uchun undan barcha kalitlar yoki qiymatlarni olishning imkoni yoâq.
WeakMap faqat quyidagi metodlarga ega:
weakMap.get(key)weakMap.set(key, value)weakMap.delete(key)weakMap.has(key)
Nega bunday cheklash? Bu texnik sabablarga koâra. Agar objekt boshqa barcha havolalarni yoâqotgan boâlsa (yuqoridagi koddagi john kabi), u avtomatik ravishda garbage-collection qilinishi kerak. Lekin texnik jihatdan tozalash qachon sodir boâlishi aniq belgilanmagan.
JavaScript mexanizmi buni hal qiladi. U xotira tozalashni darhol bajarishni yoki kutishni va koâproq oâchirish sodir boâlganda tozalashni tanlashi mumkin. Shunday qilib, texnik jihatdan WeakMap ning joriy element soni nomaâlum. Mexanizm uni tozalagan boâlishi yoki yoâq, yoki qisman bajargan boâlishi mumkin. Shu sababdan barcha kalitlar/qiymatlarni oladigan metodlar qoâllab-quvvatlanmaydi.
Endi, bizga bunday maâlumotlar strukturasi qayerda kerak?
Foydalanish holati: qoâshimcha maâlumotlar
WeakMap ning asosiy qoâllanish sohasi qoâshimcha maâlumotlar saqlashidir.
Agar biz boshqa kodga âtegishliâ boâlgan objekt bilan ishlayotgan boâlsak, hatto uchinchi tomon kutubxonasi boâlsa ham, va u bilan bogâliq baâzi maâlumotlarni saqlashni istasak, bu faqat objekt tirik boâlgan vaqtda mavjud boâlishi kerak â u holda WeakMap aynan kerak boâlgan narsadir.
Biz maâlumotlarni WeakMap ga qoâyamiz, objektni kalit sifatida ishlatamiz va objekt garbage collection qilinganda, bu maâlumotlar ham avtomatik ravishda yoâqoladi.
weakMap.set(john, "maxfiy hujjatlar");
// agar john o'lsa, maxfiy hujjatlar avtomatik ravishda yo'q qilinadi
Misolga qaraylik.
Misol uchun, bizda foydalanuvchilar uchun tashrif sonini saqlaydigan kod bor. Maâlumot xaritada saqlanadi: foydalanuvchi objekti kalit, tashrif soni esa qiymatdir. Foydalanuvchi ketganda (uning objekti garbage collection qilinganda), biz ularning tashrif sonini saqlashni xohlamaymiz.
Mana Map bilan sanash funktsiyasining misoli:
// ð visitsCount.js
let visitsCountMap = new Map(); // map: user => visits count
// tashrif sonini oshirish
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
Va mana kodning boshqa qismi, ehtimol uni ishlatadigan boshqa fayl:
// ð main.js
let john = { name: "John" };
countUser(john); // uning tashriflarini sanash
// keyinroq john bizni tark etadi
john = null;
Endi john objekti garbage collection qilinishi kerak, lekin xotirada qoladi, chunki u visitsCountMap da kalit.
Foydalanuvchilarni olib tashlaganimizda visitsCountMap ni tozlashimiz kerak, aks holda u xotirada cheksiz oâsib boradi. Bunday tozalash murakkab arxitekturalarda zerikarli vazifaga aylanishi mumkin.
Buning oârniga WeakMap ga oâtish orqali bundan qochishimiz mumkin:
// ð visitsCount.js
let visitsCountMap = new WeakMap(); // weakmap: user => visits count
// tashrif sonini oshirish
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
Endi visitsCountMap ni tozlashimiz shart emas. john objekti WeakMap ning kaliti sifatidagidan boshqa barcha yoâllar bilan yetib borilmaydigan boâlgandan soâng, u WeakMap dan shu kalit boâyicha maâlumotlar bilan birga xotiradan olib tashlanadi.
Foydalanish holati: keshlash
Yana bir keng tarqalgan misol â keshlash. Biz funktsiya natijalarini saqlashimiz (âkeshlashâ) mumkin, shunda bir xil objekt uchun kelajakdagi chaqiruvlar uni qayta ishlatishi mumkin.
Bunga erishish uchun biz Map dan foydalanishimiz mumkin (optimal boâlmagan stsenari):
// ð cache.js
let cache = new Map();
// natijani hisoblash va eslab qolish
function process(obj) {
if (!cache.has(obj)) {
let result = /* obj uchun natijani hisoblash */;
cache.set(obj, result);
}
return cache.get(obj);
}
// Endi biz process() ni boshqa faylda ishlatamiz:
// ð main.js
let obj = {/* aytaylik bizda objekt bor */};
let result1 = process(obj); // hisoblandi
// ...keyinroq, kodning boshqa joyidan...
let result2 = process(obj); // keshdan olingan eslab qolingan natija
// ...keyinroq, objekt endi kerak bo'lmaganda:
obj = null;
alert(cache.size); // 1 (Voy! Objekt hali ham keshda, xotirani egallaydi!)
Bir xil objekt bilan process(obj) ning bir necha marta chaqirilishida, u natijani faqat birinchi marta hisoblaydi, keyin uni cache dan oladi. Salbiy tomoni shundaki, objekt endi kerak boâlmaganda cache ni tozlashimiz kerak.
Agar Map ni WeakMap bilan almashtirsak, bu muammo yoâqoladi. Keshlangan natija objekt garbage collection qilingandan keyin xotiradan avtomatik ravishda olib tashlanadi.
// ð cache.js
let cache = new WeakMap();
// natijani hisoblash va eslab qolish
function process(obj) {
if (!cache.has(obj)) {
let result = /* obj uchun natijani hisoblash */;
cache.set(obj, result);
}
return cache.get(obj);
}
// ð main.js
let obj = {/* biror objekt */};
let result1 = process(obj);
let result2 = process(obj);
// ...keyinroq, objekt endi kerak bo'lmaganda:
obj = null;
// cache.size ni olib bo'lmaydi, chunki bu WeakMap,
// lekin u 0 yoki tez orada 0 bo'ladi
// obj garbage collection qilinganda, keshlangan ma'lumotlar ham olib tashlanadi
WeakSet
WeakSet xuddi shunday harakat qiladi:
- Bu
Setga oâxshash, lekin bizWeakSetga faqat objektlar qoâshishimiz mumkin (primitivlar emas). - Objekt toâplamda boshqa joydan yetib boriladigan boâlgancha mavjud.
Setkabi, uadd,hasvadeleteni qoâllab-quvvatlaydi, lekinsize,keys()va iteratsiyalar yoâq.
âZaifâ boâlib, u ham qoâshimcha saqlash vazifasini bajaradi. Lekin ixtiyoriy maâlumotlar uchun emas, balki âha/yoâqâ faktlari uchun. WeakSet dagi aâzolik objekt haqida biror narsani anglatishi mumkin.
Misol uchun, biz saytimizga tashrif buyurganlarni kuzatib borish uchun foydalanuvchilarni WeakSet ga qoâshishimiz mumkin:
let visitedSet = new WeakSet();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
visitedSet.add(john); // John bizga tashrif buyurdi
visitedSet.add(pete); // Keyin Pete
visitedSet.add(john); // John yana
// visitedSet da endi 2 ta foydalanuvchi bor
// John tashrif buyurdimi?
alert(visitedSet.has(john)); // true
// Mary tashrif buyurdimi?
alert(visitedSet.has(mary)); // false
john = null;
// visitedSet avtomatik ravishda tozlanadi
WeakMap va WeakSet ning eng muhim cheklovi â iteratsiyalarning yoâqligi va barcha joriy tarkibni olishning imkoni yoâqligidir. Bu noqulay koârinishi mumkin, lekin WeakMap/WeakSet ning asosiy ishini bajarishga toâsqinlik qilmaydi â boshqa joyda saqlanadigan/boshqariladigan objektlar uchun maâlumotlarning âqoâshimchaâ saqlash joyi boâlish.
Xulosa
WeakMap â bu Map ga oâxshash toâplam boâlib, faqat objektlarga kalitlar sifatida ruxsat beradi va ular boshqa yoâllar bilan yetib borilmaydigan boâlganda ular bilan bogâliq qiymat bilan birga olib tashlaydi.
WeakSet â bu Set ga oâxshash toâplam boâlib, faqat objektlarni saqlaydi va ular boshqa yoâllar bilan yetib borilmaydigan boâlganda ularni olib tashlaydi.
Ularning asosiy afzalliklari shundaki, ular objektlarga zaif havola qiladi, shuning uchun ular garbage collector tomonidan osonlikcha olib tashlanishi mumkin.
Bu clear, size, keys, values⦠ga qoâllab-quvvatlash yoâqligining narxiga keladi.
WeakMap va WeakSet âasosiyâ objekt saqlashga qoâshimcha ravishda âikkinchi darajaliâ maâlumotlar strukturalari sifatida ishlatiladi. Objekt asosiy saqlashdan olib tashlangandan soâng, agar u faqat WeakMap ning kaliti yoki WeakSet da topilsa, u avtomatik ravishda tozlanadi.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepenâ¦)