weakmap 不直接存储节点选区状态,而是以 dom 节点为键、序列化 range 数据为值实现弱引用缓存;因其键弱引用,dom 节点被回收时条目自动失效,避免内存泄漏;需手动序列化/反序列化 anchornode、offset 等字段,并校验节点有效性后恢复选区。

WeakMap 本身不直接存储“节点选区状态”,它只提供一种以 DOM 节点为键、任意值为值的弱引用映射能力。真正存储选区(Selection / Range)状态,需要你主动把当前选区信息(如 anchorNode、focusNode、offset 等)序列化后存入 WeakMap,且必须确保键是真实的 DOM 节点(比如 contenteditable 元素或其子节点)。
为什么用 WeakMap 存储选区状态?
富文本编辑器中,用户频繁操作 DOM(插入、删除、替换节点),若用普通 Map 或对象保存选区,容易因持有对已移除节点的强引用而造成内存泄漏或状态错乱。WeakMap 的键是弱引用,当对应 DOM 节点被 GC 回收时,该条目自动失效,无需手动清理 —— 这对动态内容区域非常安全。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
如何用 WeakMap 记录和恢复选区?
核心思路:把编辑器容器或关键文本节点作为键,把序列化的 Range 信息(非 Range 实例本身)作为值。Range 对象不能直接存进 WeakMap 作值(它不是弱引用目标),但可以安全地存字符串、数字、对象等。
- 记录选区:监听 focus/selectionchange,获取当前 Selection,提取关键字段(anchorNode、anchorOffset、focusNode、focusOffset),检查 anchorNode/focusNode 是否在编辑器内,再以其中一个稳定节点(如编辑器根节点)为 key,存入结构化数据
-
恢复选区:调用
window.getSelection().removeAllRanges()后,用之前存的 anchorNode(需确保仍存在)、anchorOffset 等重建 Range,并 addRange -
注意节点有效性:恢复前应检查 anchorNode 是否 still in document(
anchorNode?.ownerDocument?.body.contains(anchorNode)),否则跳过或 fallback 到默认位置
一个轻量示例(不依赖框架)
假设编辑器容器是 <div id="editor" contenteditable>:
<font size="2">
<pre class="brush:php;toolbar:false;">
const selectionCache = new WeakMap();
function saveSelection(editor) {
const sel = window.getSelection();
if (!sel.rangeCount || !sel.anchorNode) return;
// 只缓存锚点在编辑器内的选区
if (!editor.contains(sel.anchorNode)) return;
const range = sel.getRangeAt(0);
const data = {
anchorNode: range.startContainer,
anchorOffset: range.startOffset,
focusNode: range.endContainer,
focusOffset: range.endOffset,
};
// ✅ editor 是 DOM 节点,可作 WeakMap 键
selectionCache.set(editor, data);
}
function restoreSelection(editor) {
const data = selectionCache.get(editor);
if (!data) return;
const { anchorNode, anchorOffset, focusNode, focusOffset } = data;
// 检查节点是否还活着
if (!anchorNode?.ownerDocument?.body.contains(anchorNode) ||
!focusNode?.ownerDocument?.body.contains(focusNode)) {
return;
}
const range = document.createRange();
range.setStart(anchorNode, anchorOffset);
range.setEnd(focusNode, focusOffset);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
// 使用:编辑前保存,切换 tab 或 blur 后恢复
editor.addEventListener('blur', () => saveSelection(editor));
editor.addEventListener('focus', () => restoreSelection(editor));
</pre>
</font>
<h3>进阶建议</h3>
<ul>
<li>若需支持多光标或多个独立编辑区域,可用每个区域根节点分别作为 WeakMap 键,互不干扰</li>
<li>避免将 TextNode 直接作 key(尤其动态生成的),优先选父 Element(如段落 <p>),更稳定</p>
</li>
<li>不要存 Selection 或 Range 实例本身——它们会阻止 GC,且跨文档无效;只存可序列化的描述性字段</li>
<li>配合 MutationObserver 监听 DOM 变动时,可主动清空失效缓存(非必须,WeakMap 已自动处理节点销毁)</li>
</ul>
</div>
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










