闭包不直接实现无限撤销,而是通过封装撤销栈、命令上下文和操作分组状态,为命令模式等策略提供安全、可控、隔离的执行环境。

闭包本身不直接提供“无限撤销”能力,但它为构建可预测、隔离、带私有状态的撤销系统提供了关键支撑。真正实现无限级别撤销的是状态管理策略(如命令模式、事件溯源或双栈结构),而闭包负责安全封装这些策略中的核心变量与逻辑边界。
用闭包封装撤销栈与当前状态
将 undo/redo 栈、当前编辑版本、分组标识等敏感状态封闭在函数作用域内,外部无法篡改,避免全局污染和意外重置。
- 定义一个工厂函数,返回包含 undoStack、redoStack 和 currentState 的操作对象
- 所有 push/pop 操作都通过闭包内方法执行,确保栈变更原子且受控
- 例如:const historyManager = createHistoryManager(initialState),后续调用 historyManager.undo() 不会暴露内部栈结构
结合命令模式,让每个命令自带撤销/重做逻辑
闭包使命令对象能天然捕获执行时所需的上下文(如节点 ID、旧属性值、父容器引用),无需依赖外部状态快照。
- 每个命令实例(如 MoveNodeCommand)在创建时用闭包记住操作前的位置、目标父节点等关键数据
- undo() 方法直接使用这些被捕获的值还原状态,不查表、不比对、不深克隆
- 多个命令可被同一组闭包变量(如 eventGroupID)关联,实现原子化撤销(一次撤回拖拽+缩放+重命名)
利用闭包实现“操作分组”的上下文隔离
用户一次鼠标拖拽可能触发数十次位置更新事件,但应视为单个可撤销动作。闭包帮助维持临时分组状态,直到操作结束。
- 开始拖拽时调用 startGroup(),内部用闭包保存当前 groupID 和时间戳
- 所有中间变更事件自动带上该 groupID,由闭包内过滤器识别并聚合
- 松开鼠标后调用 endGroup(),闭包将这一组事件打包为一个复合命令压入 undoStack
规避内存泄漏:闭包引用需可控释放
无限撤销意味着栈可能持续增长,而每个命令若长期持有大对象引用(如整个画布快照),就会引发内存问题。闭包在此处需主动管理生命周期。
- 命令中只捕获必要字段(如 id、x/y 偏移量),而非整个节点对象或渲染上下文
- 撤销栈达到上限时,自动清理最老命令——闭包确保其内部引用随命令对象一起被垃圾回收
- 对大型资源(如图片、路径点数组),采用弱引用或 ID 映射方式,避免闭包隐式延长生命周期










