Использование узла DOM в качестве ключа в WeakMap

При работе с WeakMap Я столкнулся со сценарием, в котором я нахожу довольно загадочным: скажем, у меня есть DOM-узел с некоторыми данными, которые я хочу сохранить, и я храню его в WeakMap использование самого элемента / узла в качестве ключа и произвольных данных в качестве значения.

Между хранением и извлечением записи из WeakMap, DOM узел изменен: скажем, это id атрибут был обновлен. Я ожидаю, что .get(<Node>) вернусь undefined поскольку узел был видоизменен, но он все равно каким-то образом возвращает его.

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

Мой вопрос: почему изменение узла DOM, который использовался в качестве ключа для хранения произвольных данных, в WeakMap, не возвращается undefined? Вот пример проверки концепции с инструкциями по воспроизведению поведения:

  1. Нажмите "Элемент магазина"
  2. Нажмите "Получить элемент", чтобы убедиться, что элемент действительно хранится в WeakMap
  3. Нажмите на "Изменить элемент". Элемент должен иметь свой id атрибут обновлен.
  4. Нажмите "Получить элемент": даже если элемент был изменен, он все равно может получить значение, установленное с исходным элементом.
  5. Нажмите на "Уничтожить и воссоздать элемент". Узел удаляется из DOM, и его outerHTML используется для создания идентичного элемента.
  6. Нажмите "Получить элемент": WeakMap правильно сообщает, что ничего не найдено, так как мы используем совершенно новый узел DOM в качестве ключа.

const map = new WeakMap();

// Store element in WeakMap
document.getElementById('set').addEventListener('click', () => {
  const el = document.querySelector('#content > div');
  map.set(el, el.outerHTML);
  console.log('Element stored in WeakMap');
});

// Retrieve element from WeakMap
document.getElementById('get').addEventListener('click', () => {
  const el = document.querySelector('#content > div');
  const elHTML = map.get(el);
  if (elHTML)
    console.log(`Element found in WeakMap, it's data: ${elHTML}`);
  else
    console.log('Element not found in Weakmap!');
});

// Mutate the DOM node, let's say by giving it a new unique ID
let n = 0;
document.getElementById('mutate').addEventListener('click', () => {
  document.querySelector('#content > div').id = `test${n}`;
  console.log(`Element ID updated to: "test${n}"`);
  n++;
});

// Destroy and recreate element
document.getElementById('destroy_and_recreate').addEventListener('click', () => {
  const target = document.querySelector('#content > div');
  const targetHTML = target.outerHTML;
  target.remove();
  document.getElementById('content').innerHTML = targetHTML;
  console.log('Element destroyed and recreated');
});
<section id="content">
  <div id="test">Lorem ipsum dolor sit amet</div>
</section>
<hr />
<button type="button" id="set">Store element</button>
<button type="button" id="get">Retrieve element</button>
<hr />
<button type="button" id="mutate">Mutate element</button>
<button type="button" id="destroy_and_recreate">Destroy and recreate element</button>

1 ответ

Решение

По той же причине, что {} !== {}; не важно, что два объекта имеют одинаковые свойства или, если они были изменены, только в том случае, если они действительно являются одним и тем же объектом - технически, в одном и том же месте в памяти. Если вы хотите сравнить объекты по свойствам и значениям, используйте функцию с глубоким равенством, подобную той, что в Lodash.

Другие вопросы по тегам