Открыть сервисСервис

WeakMap

WeakMap — это встроенный объект в языке программирования JavaScript, реализующий ассоциативный массив (коллекцию «ключ-значение»), в котором ключами могут быть только объекты (или неиспользуемые символы в некоторых версиях), а значениями — произвольные типы данных. Основная особенность WeakMap заключается в том, что он хранит «слабые» ссылки на ключи-объекты, что позволяет сборщику мусора автоматически удалять пары «ключ-значение», если на объект-ключ больше нет других ссылок в программе. Это делает WeakMap полезным для хранения дополнительных, приватных или временных данных, связанных с объектами, без риска утечки памяти.

История и стандартизация

WeakMap был впервые представлен в спецификации ECMAScript 2015 (ES6) как часть набора новых коллекций, наряду с Map, Set и WeakSet. Идея «слабых» ссылок существовала в других языках программирования (например, в Java — WeakHashMap, в C# — WeakReference), но в JavaScript была реализована с учётом особенностей сборки мусора и отсутствия прямого управления памятью. Введение WeakMap решило проблему утечек памяти, возникающих при использовании обычных объектов или Map для хранения данных, связанных с DOM-элементами или другими временными объектами. Стандарт ECMAScript 2021 (ES12) расширил WeakMap, разрешив использовать в качестве ключей символы (Symbol), но только те, которые зарегистрированы в глобальном реестре символов (well-known symbols). В 2023 году предложение «Symbols as WeakMap keys» (Symbols.asWeakMapKeys) было включено в черновик ECMAScript, что позволило использовать любые символы в качестве ключей.

Основные характеристики

Ключи и значения

  • Ключи: только объекты (включая функции и массивы) или символы (в современных реализациях). Примитивные типы (числа, строки, булевы значения, null, undefined) не допускаются.
  • Значения: любые типы данных, включая примитивы, объекты, функции, а также другие WeakMap или Map.

Слабые ссылки

WeakMap хранит «слабые» ссылки на ключи-объекты. Это означает, что наличие пары в WeakMap не препятствует сборщику мусора удалить объект-ключ, если на него нет других «сильных» ссылок из программы. Как только объект-ключ удаляется сборщиком мусора, соответствующая пара «ключ-значение» автоматически удаляется из WeakMap. Это поведение недетерминировано — точное время удаления зависит от реализации сборщика мусора в конкретном движке JavaScript.

Отсутствие итерации

WeakMap не предоставляет методов для итерации по своим элементам (например, forEach, keys(), values(), entries()), а также не имеет свойства size. Это связано с тем, что содержимое WeakMap может быть изменено сборщиком мусора в любой момент, что сделало бы итерацию ненадёжной. Для проверки наличия ключа используются методы has() и get().

Методы WeakMap

WeakMap имеет четыре метода, аналогичных методам Map:

  • set(key, value) — добавляет пару «ключ-значение» в WeakMap. Если ключ уже существует, обновляет значение. Возвращает сам WeakMap (для цепочечного вызова).
  • get(key) — возвращает значение, связанное с ключом, или undefined, если ключ отсутствует.
  • has(key) — возвращает true, если ключ присутствует в WeakMap, иначе false.
  • delete(key) — удаляет пару по ключу. Возвращает true, если удаление прошло успешно, или false, если ключ не найден.

Пример использования: ``javascript const wm = new WeakMap(); const obj = {}; wm.set(obj, 'some value'); console.log(wm.get(obj)); // 'some value' console.log(wm.has(obj)); // true wm.delete(obj); console.log(wm.has(obj)); // false ``

Применение

Хранение приватных данных

WeakMap часто используется для хранения приватных свойств объектов, которые не должны быть доступны извне. Поскольку ключом является сам объект, а значение — приватные данные, эти данные не могут быть получены без ссылки на объект. Это позволяет реализовать паттерн «приватные поля» до появления нативной поддержки приватных полей в классах (ES2020).

Пример: ``javascript const privateData = new WeakMap(); class User { constructor(name) { privateData.set(this, { name }); } getName() { return privateData.get(this).name; } } ``

Ассоциация данных с DOM-элементами

В веб-разработке WeakMap часто применяется для связывания дополнительных данных с DOM-элементами без создания циклических ссылок, которые могли бы привести к утечкам памяти. Когда элемент удаляется из DOM, сборщик мусора может освободить память, и WeakMap автоматически очистит связанные данные.

Пример: ``javascript const elementData = new WeakMap(); const button = document.querySelector('button'); elementData.set(button, { clicks: 0 }); button.addEventListener('click', () => { const data = elementData.get(button); data.clicks++; }); ``

Кэширование результатов функций

WeakMap может использоваться для кэширования результатов вызовов функций, где ключом является аргумент-объект. Если объект больше не используется, кэш автоматически очищается.

Пример: ``javascript const cache = new WeakMap(); function computeExpensive(obj) { if (cache.has(obj)) { return cache.get(obj); } const result = expensiveCalculation(obj); cache.set(obj, result); return result; } ``

Управление памятью в библиотеках и фреймворках

Многие современные JavaScript-библиотеки (например, Vue.js, React) используют WeakMap для хранения внутреннего состояния компонентов, связанного с DOM-элементами или виртуальными узлами, чтобы избежать утечек памяти при удалении компонентов.

Ограничения и особенности

  • Нельзя проверить размер: отсутствие свойства size и методов итерации делает WeakMap непригодным для случаев, когда нужно перебрать все элементы или узнать количество записей.
  • Недетерминированное удаление: нельзя полагаться на то, что пара будет удалена сразу после потери последней сильной ссылки на ключ. Удаление происходит только во время циклов сборки мусора.
  • Только объекты и символы в качестве ключей: это ограничение делает WeakMap менее универсальным, чем Map, но необходимым для реализации слабых ссылок.
  • Совместимость: WeakMap поддерживается во всех современных браузерах и средах выполнения JavaScript (Node.js, Deno). В старых средах (например, Internet Explorer 11) поддержка может отсутствовать или быть неполной.

Сравнение с Map

ХарактеристикаMapWeakMap
Типы ключейЛюбые (примитивы, объекты)Только объекты и символы
Слабые ссылкиНет (сильные ссылки)Да (слабые ссылки на ключи)
ИтерацияДа (forEach, keys(), values(), entries())Нет
Свойство sizeДаНет
Утечки памятиВозможны при хранении ссылок на временные объектыМинимизированы
ПроизводительностьНемного быстрее для итерацийНемного быстрее для операций с одним ключом

Интересные факты

  • В спецификации ECMAScript 2021 (ES12) появилась возможность использовать в качестве ключей WeakMap «известные символы» (well-known symbols), такие как Symbol.toStringTag или Symbol.iterator, но только в том случае, если они зарегистрированы в глобальном реестре.
  • В 2023 году было принято предложение «Symbols as WeakMap keys», которое разрешило использовать любые символы в качестве ключей, что расширило возможности WeakMap.
  • WeakMap не является единственной реализацией слабых ссылок в JavaScript. Существует также WeakSet, который хранит только объекты (слабые ссылки) без значений, а также глобальный объект WeakRef (ES2021), позволяющий создавать слабые ссылки на произвольные объекты.

Источники

  • Спецификация ECMAScript 2015 (ES6) — раздел «WeakMap Objects»
  • Спецификация ECMAScript 2021 (ES12) — раздел «WeakMap»
  • MDN Web Docs: WeakMap — JavaScript
  • Статья «WeakMap and WeakSet» на JavaScript.info
  • Предложение «Symbols as WeakMap keys» (TC39 Proposal)
Заметили ошибку или не согласны с информацией в статье? Напишите нам support@bfometr.ru