Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  1. Главная
  2. Категории
  3. Языки программирования
  4. JavaScript
  5. WeakMap против утечек в event-листенерах: под капотом DOM и кэш

WeakMap против утечек в event-листенерах: под капотом DOM и кэш

Запланировано Прикреплена Закрыта Перенесена JavaScript
weakmapутечки памятиevent listeners
1 Сообщения 1 Постеры 3 Просмотры
  • Сначала старые
  • Сначала новые
  • По количеству голосов
Ответить
  • Ответить, создав новую тему
Авторизуйтесь, чтобы ответить
Эта тема была удалена. Только пользователи с правом управления темами могут её видеть.
  • hannadevH В сети
    hannadevH В сети
    hannadev
    написал отредактировано
    #1

    Event-листенеры на DOM-нодах часто оставляют утечки памяти. Объект удаляется из DOM, а слушатель держит на него жесткую ссылку через замыкание. WeakMap решает это - слабые ссылки позволяют GC собрать мусор автоматически.

    Это критично для SPA с динамическим DOM. Без правильного управления кэш пользовательских данных разрастается, и приложение тормозит. Разберем, как WeakMap чистит за собой, на примерах реального кода.

    Классическая утечка: жесткие ссылки в слушателях

    Когда добавляешь addEventListener, колбэк захватывает DOM-ноду в замыкании. Нода удаляется из дерева, но слушатель в нейтральной зоне держит ссылку. GC не может ничего собрать - объект висит в памяти зря.

    В типичном сценарии SPA рендеришь список пользователей. Каждый элемент с клик-хендлером, который тянет за собой данные профиля. Прокручиваешь список, элементы уходят из вида, но память не освобождается. Через час сессии JS-куча раздувается вдвое.

    Проблема усугубляется кэшем. Хранишь userData по ноде в Map - ключ тоже жесткий. Даже если нода ушла, Map не отпустит. Результат: утечка растет линейно с действиями юзера.

    • addEventListener без remove: Хендлер держит this и замыкания. Удаляй вручную в cleanup, но забьешь - привет утечка.
    • Замыкания с данными: function handler() { use(bigUserObject); } - bigUserObject не уйдет.
    • Глобальный кэш: const cache = new Map(); cache.set(node, data); - node жив до конца приложения.
    Подход Ссылки на DOM GC работает? Риск утечки
    Map + жесткие ключи Жесткие Нет Высокий
    WeakMap Слабые Да Низкий
    Ручной removeEventListener Жесткие до cleanup Зависит Средний

    WeakMap под капотом: слабые ключи для нод

    WeakMap - это Map, где ключи только объекты, и ссылки на них слабые. GC видит, что на ключ нет других ссылок извне - собирает и ключ, и значение. Идеально для кэша по DOM-нодам.

    Под капотом WeakMap не хранит счетчик ссылок явно. Браузерный V8 или SpiderMonkey интегрируют его с марк-энд-свип. Когда нода удалена из DOM и нет других ссылок, WeakMap автоматически ее сбрасывает. Нет нужды в ручной чистке.

    Пример с event-листенером. Вместо хранения хендлера в глобале, кэшируй данные по ноде в WeakMap. Хендлер захватывает WeakMap - но это не жесткая ссылка на ноду.

    const listeners = new WeakMap();
    
    function attachHandler(node, userData) {
      listeners.set(node, userData);
      node.addEventListener('click', function() {
        const data = listeners.get(node);
        console.log(data);
      });
    }
    
    • Нода удалена: GC видит, что на node нет ссылок кроме WeakMap - чистит node и значение.
    • Автоматично: Нет setTimeout для cleanup, нет проверки isMounted.
    • Масштабируемо: тысячи нод - память чистится сама.

    Кэш пользовательских данных без костылей

    Обычный паттерн: кэш профилей по ноде аватара. Map(node => userProfile). Node уходит - профиль висит. WeakMap меняет правила: кэш живет ровно столько, сколько нода.

    В реальном проекте SPA с виртуальным скроллом. Рендеришь 1000+ строк таблицы. Каждая с фото, данными юзера. Без WeakMap кэш сожрет гигабайт за сессию. С WeakMap - память стабильна.

    Интеграция с событиями. Хендлер клика по аватару тянет tooltip с данными. Данные из WeakMap - если нода жива, данные на месте. Ушла нода - tooltip не сломается, просто undefined.

    const userCache = new WeakMap();
    
    document.addEventListener('click', (e) => {
      if (e.target.dataset.userId) {
        const node = e.target.closest('.avatar');
        const profile = userCache.get(node);
        if (profile) showTooltip(profile);
      }
    });
    
    • Производительность: O(1) доступ, как в Map.
    • Нюанс: Значения тоже могут быть объектами - GC их тоже проверит.
    • Плюс WeakRef: Для значений, если нужно отложенное чтение.
    Map WeakMap Пример использования
    Жесткие ключи Слабые ключи Кэш по DOM-элементам
    Нужно чистить вручную Авто-GC Event-данные
    Глобальные ключи Только объекты Пользовательские профили

    Реальные грабли и как их обойти

    Новички лепят WeakMap куда попало, забывая, что ключи - только объекты. Строка в ключ - Error. Или думают, что WeakMap вечный - нет, он чистится при GC.

    Частая ошибка: хендлер захватывает переменную извне замыкания. listeners.get(node) внутри, но if (externalVar) {} - externalVar держит все. Делай чистые функции.

    В React-подобных фреймворках lifecycle-хуки. useEffect добавляет слушатель - в cleanup removeEventListener. Но с WeakMap можно не чистить: GC сам разберется. Только если хендлер не захвачен родителем.

    • Грабли 1: const strongRef = node; перед WeakMap - убивает слабость.
    • Грабли 2: Массив нод в глобале - все утечки вернулись.
    • Тест: Chrome DevTools > Memory > Heap snapshot. С WeakMap delta = 0.

    Когда WeakMap не панацея

    WeakMap спасает от 80% утечек в DOM-кэше. Но если данные нужны после удаления ноды - комбинируй с IndexedDB или sessionStorage. Или WeakRef для ленивой загрузки.

    Осталось за кадром: полифиллы для старых браузеров, WeakMap в Node.js воркерах. Подумай, как интегрировать с ResizeObserver - там тоже ноды летают. Тестируй в продакшене с реальными данными.

    1 ответ Последний ответ
    0

    Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.

    Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.

    С вашими комментариями этот пост мог бы стать ещё лучше 💗

    Зарегистрироваться Войти

    Категории

    • Главная
    • Новости
    • Фронтенд
    • Бекенд
    • Языки программирования

    Контакты

    • Сотрудничество
    • info@exlends.com

    © 2024 - 2026 ExLends, Inc. Все права защищены.

    Политика конфиденциальности
    • Войти

    • Нет учётной записи? Зарегистрироваться

    • Войдите или зарегистрируйтесь для поиска.
    • Первое сообщение
      Последнее сообщение
    0
    • Лента
    • Категории
    • Последние
    • Метки
    • Популярные
    • Пользователи
    • Группы