闭包封装撤销栈实现富文本编辑器历史管理,通过立即执行函数隐藏 undoStack/redoStack,提供 pushState、undo、redo 方法,并捕获光标、选区等状态,限制栈大小防内存泄漏。

在富文本编辑器中用闭包保持撤销栈,核心是把栈状态封装在闭包作用域内,避免外部意外修改,同时让 undo/redo 操作函数能持续访问和更新同一份栈数据。
用闭包封装撤销栈和操作逻辑
不把 undoStack 和 redoStack 暴露为全局变量或实例属性,而是放在一个立即执行函数或构造函数内部,让所有操作方法(如 pushState、undo、redo)共享该作用域:
function createHistoryManager() {
const undoStack = [];
const redoStack = [];
<p>return {
pushState(state) {
undoStack.push(JSON.parse(JSON.stringify(state))); // 深拷贝避免引用污染
redoStack.length = 0; // 新操作清空重做栈
},
undo() {
if (undoStack.length </p><p>// 编辑器初始化时创建独立历史管理器
const history = createHistoryManager();
</p>绑定到编辑器事件时保留闭包上下文
监听输入、格式化、粘贴等变更事件时,确保调用的是同一个 history 实例的方法,而不是重复创建或丢失引用:
- 不要在每次事件回调里重新
createHistoryManager() - 避免将
history.undo直接赋给按钮onclick而未绑定this—— 闭包本身不依赖this,但若方法内用了this就需.bind(history)或箭头函数 - 推荐用显式调用:
button.addEventListener('click', () => history.undo())
处理富文本特有的状态快照问题
富文本状态不只是 HTML 字符串,还需捕获光标位置、选区、焦点状态等。闭包内可配合 getSelectionInfo() 等工具函数统一快照:
function createHistoryManager(editor) {
const undoStack = [];
const redoStack = [];
<p>function captureState() {
return {
html: editor.innerHTML,
selection: getSelectionInfo(editor), // 自定义函数,返回 {anchorNode, anchorOffset, ...}
scrollTop: editor.scrollTop
};
}</p><p>return {
record() {
undoStack.push(captureState());
redoStack.length = 0;
},
undo() {
// ...同上,但还原时要 restoreSelection()
const prev = undoStack[undoStack.length - 1];
if (prev) {
editor.innerHTML = prev.html;
restoreSelection(editor, prev.selection);
editor.scrollTop = prev.scrollTop;
}
return prev;
}
};
}
</p>避免闭包导致的内存泄漏
撤销栈无限增长会拖慢性能甚至卡死。闭包虽然保护了数据,但也延长了对象生命周期。需主动限制栈大小:
- 在
pushState中检查长度,超出阈值时undoStack.shift()删除最早记录 - 对频繁触发的操作(如连续输入),可节流合并:只在输入暂停 300ms 后记录一次
- 不用
JSON.stringify存 DOM 树,改用轻量结构(如只存 diff 或关键属性)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











