Привет, Хабр!
Когда дело доходит до коллекций данных в JavaScript, большинство разработчиков сразу вспоминают про массивы, объекты, Map
или Set
. Но есть и другие, менее известные структуры данных, которые можно назвать «инструментами для особых случаев» — это WeakMap и WeakSet.
WeakMap и WeakSet — это структуры, которые созданы для работы с объектами. Их основная фичи — слабые ссылки, благодаря которым можно избежать утечек памяти. Эти структуры подчищают за собой автоматически, когда объект, используемый в них, становится недостижимым.
Однако их область применения не ограничивается только управлением памятью. WeakMap и WeakSet позволяют:
Хранить данные, которые исчезают сами, без явного удаления.
Создавать скрытые хранилища для приватных данных, не изменяя сам объект.
Автоматическое удаление временных данных, связанных с объектами, например, кэшей.
WeakMap
WeakMap
— это коллекция пар ключ‑значение, где:
Ключами могут быть только объекты.
Значениями могут быть любые данные, от строк до функций.
Ключи имеют слабую ссылку, что позволяет сборщику мусора удалять их, если они больше нигде не используются.
Основное отличие от обычного Map
в том, что WeakMap
заботится о памяти самостоятельно. Не нужно вручную удалять данные, когда объект становится ненужным — это происходит автоматически.
С WeakMap
работают всего четыре метода:
set(key, value)
— добавляет пару ключ‑значение.get(key)
— возвращает значение, связанное с ключом.has(key)
— проверяет наличие ключа.delete(key)
— удаляет пару ключ‑значение.
Пример использования:
const weakMap = new WeakMap();
let user = { name: "Alice" };
weakMap.set(user, "Приватные данные пользователя");
console.log(weakMap.get(user)); // "Приватные данные пользователя"
user = null;
// После этого объект автоматически удаляется из WeakMap сборщиком мусора.
Примеры применения
WeakMap отлично подходит для хранения приватных данных внутри объектов, не изменяя их структуру.
const privateData = new WeakMap();
class User {
constructor(name) {
this.name = name;
privateData.set(this, { permissions: [] });
}
getPermissions() {
return privateData.get(this).permissions;
}
addPermission(permission) {
privateData.get(this).permissions.push(permission);
}
}
const user = new User("Bob");
user.addPermission("admin");
console.log(user.getPermissions()); // ["admin"]
// Если объект user будет удалён, его данные из privateData также исчезнут.
А если ваш код обрабатывает объекты, и нужно сохранять результаты обработки, WeakMap
позволяет это сделать без риска утечек памяти.
const cache = new WeakMap();
function processData(obj) {
if (cache.has(obj)) {
return cache.get(obj); // Используем закэшированные данные
}
// Сложные вычисления
const result = { processed: true };
cache.set(obj, result);
return result;
}
let data = { id: 1 };
console.log(processData(data)); // { processed: true }
console.log(processData(data)); // { processed: true }
data = null; // Кэш автоматически очищается.
WeakSet
WeakSet
— это коллекция только объектов, которые хранятся по слабым ссылкам. Как и в случае с WeakMap
, если объект становится недостижимым, он автоматически удаляется из WeakSet
.
Основное предназначение WeakSet
— отслеживание уникальных объектов без риска их «залипания» в памяти.
Методов у WeakSet
ещё меньше, чем у WeakMap
:
add(value)
— добавляет объект.has(value)
— проверяет наличие объекта.delete(value)
— удаляет объект.
Пример использования:
const weakSet = new WeakSet();
let obj = { id: 1 };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
obj = null;
// Объект автоматически удаляется из WeakSet.
Примеры применения
Например, нужно гарантировать, что каждый объект обрабатывается только один раз:
const processed = new WeakSet();
function process(obj) {
if (processed.has(obj)) {
console.log("Объект уже обработан!");
return;
}
processed.add(obj);
console.log("Обрабатываю объект:", obj);
}
let task = { id: 1 };
process(task); // "Обрабатываю объект"
process(task); // "Объект уже обработан!"
task = null; // WeakSet автоматически очищается.
Так же, работая с DOM, можно использовать WeakSet
для отслеживания элементов, которым уже назначены обработчики событий:
const elementsWithHandlers = new WeakSet();
function addClickHandler(element) {
if (!elementsWithHandlers.has(element)) {
elementsWithHandlers.add(element);
element.addEventListener("click", () => console.log("Клик!"));
}
}
let div = document.createElement("div");
addClickHandler(div); // Привязываем обработчик
addClickHandler(div); // Ничего не делаем, обработчик уже добавлен
div = null; // Обработчики удаляются автоматически.
Хотя использование этих инструментов может показаться ограниченным, они становятся незаменимыми, когда вы работаете с кэшами, приватными данными или сложными DOM‑структурами.
Если есть опыт применения этих инструментов, пишите в комментариях!
Все актуальные инструменты и лучшие практики веб‑разработки можно изучить на онлайн‑курсах в Otus. В каталоге можно посмотреть список всех программ, а в календаре мепроприятий — записаться на бесплатные открытые уроки.