When working with WeakMap
I came across a scenario where I find quite puzzling: let's say I have a DOM node with some data I want to store, and I store it in a WeakMap
using the element/node itself as the key, and the arbitrary data as value.
Between storing and retrieving the entry from WeakMap
, the DOM node is changed: let's say, it's id
attribute was updated. I would expect that the .get(<Node>)
will return undefined
since the node has been mutated, but it still somehow returns it.
However, when I destroy the node in the DOM tree and re-render it—even without changing any of its attributes or properties—it is now considered a new element in WeakMap
when storing it.
My question is: why does changing a DOM node, which was used as a key to store arbitrary data, in WeakMap
, not return undefined
? Here is a proof-of-concept example, with instructions to reproduce the behaviour:
WeakMap
id
attribute updated.outerHTML
is used to create an identical looking element.WeakMap
correctly reports that nothing is found, since we are using an entirely new DOM node as a key.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>
The same reason that {} !== {}
; it’s not important that two objects have the same properties, or if they were changed, only whether they actually are the same object - technically, the same location in memory. If you want to compare objects by properties and values, use a deep equal function like the one in Lodash.