Пошук уроків, статей та іншого контенту
Колекції зі слабкими посиланнями на об'єкти-ключі, які не заважають звільненню пам'яті.
JavaScript автоматично звільняє пам'ять, зайняту об'єктом, коли на нього більше ніде немає посилань (детальніше — в останньому уроці цього модуля). Проблема: якщо звичайний Map використовує об'єкт як ключ, цей ключ вважається посиланням — навіть якщо весь інший код давно «забув» про об'єкт, Map продовжує тримати його живим, і пам'ять не звільняється.
WeakMap поводиться майже як Map, але посилання на ключі-об'єкти в ньому «слабкі» (weak) — вони не рахуються для збирача сміття. Якщо об'єкт-ключ більше ніде не використовується, він звільниться з пам'яті разом із відповідним записом у WeakMap, автоматично:
let user = { name: "Олена" };
const cache = new WeakMap();
cache.set(user, { lastVisit: Date.now() });
user = null; // більше немає посилань на оригінальний об'єкт
// запис у cache для цього об'єкта тепер може бути звільнений збирачем сміття —
// на відміну від звичайного Map, який утримував би об'єкт вічноconst weak = new WeakMap();
weak.set("string-key", "value"); // TypeError — примітиви не можна використовувати як ключНемає for...of, немає size, немає способу перерахувати всі ключі. Це не недолік дизайну, а прямий наслідок призначення: оскільки збирач сміття може видалити запис у будь-який момент, перелік вмісту був би непередбачуваним і змінювався б сам по собі між викликами.
weak.has(user); // працює
weak.get(user); // працює
weak.size; // undefined — такої властивості немає
[...weak]; // TypeError — WeakMap не ітерованийТиповий випадок використання — зберігати додаткові дані про об'єкт (наприклад, DOM-елемент), не змінюючи сам об'єкт і не турбуючись про очищення після того, як об'єкт видалили зі сторінки:
const elementMetadata = new WeakMap();
function attachMetadata(element, data) {
elementMetadata.set(element, data);
}
const button = document.querySelector("#save-btn");
attachMetadata(button, { clickCount: 0 });
// Якщо button пізніше видаляється зі сторінки (button.remove()) і втрачає всі
// інші посилання, elementMetadata автоматично звільнить пов'язаний запис —
// не потрібно вручну викликати elementMetadata.delete(button)const processedElements = new WeakSet();
function markProcessed(element) {
processedElements.add(element);
}
function isProcessed(element) {
return processedElements.has(element);
}Якщо потрібно перерахувати всі елементи колекції чи знати її розмір — WeakMap/WeakSet не підходять узагалі, це не альтернатива «на будь-який випадок». Вони вирішують вузьку, конкретну задачу: асоціювати дані з об'єктом без ризику витоку пам'яті, коли час життя об'єкта керується іншою частиною коду.
Спроба використати примітив (рядок, число) як ключ WeakMap.
Очікування, що WeakMap можна перебрати чи дізнатись його розмір.
Використання WeakMap там, де насправді потрібен звичайний Map з ручним контролем життєвого циклу записів.
WeakMap і WeakSet тримають слабкі посилання на об'єкти-ключі — вони не заважають збирачу сміття звільнити пам'ять, коли об'єкт більше нізвідки не використовується. Плата за це — відсутність ітерації й розміру. Використовуйте їх, коли потрібно прив'язати додаткові дані до об'єкта, чиїм життєвим циклом керує інший код, а не ваша колекція.
Спробуйте самостійно
Поясніть своїми словами (без коду): чому кешування результатів обчислення для DOM-елементів через WeakMap безпечніше за звичайний Map, якщо елементи можуть динамічно видалятися зі сторінки.