在 WeakMap 中使用 DOM 节点作为键
Using DOM node as key in WeakMap
在使用 WeakMap
时,我遇到了一个令我非常困惑的场景:假设我有一个 DOM 节点,其中包含一些我想存储的数据,我将其存储在 WeakMap
使用 element/node 本身作为键,任意数据作为值。
在存储和检索来自 WeakMap
的条目之间,DOM 节点已更改:比方说,它的 id
属性已更新。我希望 .get(<Node>)
会 return undefined
因为节点已经发生变异,但它仍然以某种方式 return 是它。
然而,当我销毁 DOM 树中的节点并重新渲染它时——即使不更改它的任何属性或属性——它现在被认为是 WeakMap
中的一个新元素,当存储它。
我的问题是:为什么在WeakMap
中更改用作存储任意数据的键的DOM节点,而不是returnundefined
?这是一个概念验证示例,其中包含重现该行为的说明:
- 点击"Store element"
- 单击 "Retrieve element" 验证元素确实存储在
WeakMap
- 点击 "Mutate element"。该元素应该更新其
id
属性。
- 点击"Retrieve element":即使元素发生了变异,仍然可以获取原始元素设置的值。
- 点击 "Destroy and recreate element"。节点从 DOM 中删除,其
outerHTML
用于创建外观相同的元素。
- 单击 "Retrieve element":
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>
同理{} !== {}
;两个对象是否具有相同的属性并不重要,或者如果它们被更改了,重要的只是它们是否实际上是同一个对象——从技术上讲,是内存中的相同位置。如果您想按属性和值比较对象,请使用像 Lodash 中的深度相等函数。
在使用 WeakMap
时,我遇到了一个令我非常困惑的场景:假设我有一个 DOM 节点,其中包含一些我想存储的数据,我将其存储在 WeakMap
使用 element/node 本身作为键,任意数据作为值。
在存储和检索来自 WeakMap
的条目之间,DOM 节点已更改:比方说,它的 id
属性已更新。我希望 .get(<Node>)
会 return undefined
因为节点已经发生变异,但它仍然以某种方式 return 是它。
然而,当我销毁 DOM 树中的节点并重新渲染它时——即使不更改它的任何属性或属性——它现在被认为是 WeakMap
中的一个新元素,当存储它。
我的问题是:为什么在WeakMap
中更改用作存储任意数据的键的DOM节点,而不是returnundefined
?这是一个概念验证示例,其中包含重现该行为的说明:
- 点击"Store element"
- 单击 "Retrieve element" 验证元素确实存储在
WeakMap
- 点击 "Mutate element"。该元素应该更新其
id
属性。 - 点击"Retrieve element":即使元素发生了变异,仍然可以获取原始元素设置的值。
- 点击 "Destroy and recreate element"。节点从 DOM 中删除,其
outerHTML
用于创建外观相同的元素。 - 单击 "Retrieve element":
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>
同理{} !== {}
;两个对象是否具有相同的属性并不重要,或者如果它们被更改了,重要的只是它们是否实际上是同一个对象——从技术上讲,是内存中的相同位置。如果您想按属性和值比较对象,请使用像 Lodash 中的深度相等函数。