函数声明配合原型模式使撤销重做状态机可维护、可扩展、内存可控:将操作抽象为含execute/undo/redo的命令对象,通过原型链共享方法、避免闭包泄漏,用函数声明显式控制this和继承,并统一管理合并、快照、工厂策略及栈生命周期。

函数声明配合原型模式不是为了炫技,而是让撤销重做状态机真正可维护、可扩展、内存可控。核心在于:把每个操作抽象为带 执行、撤销、重做 三方法的命令对象,用原型链统一管理共性逻辑,避免重复代码和闭包内存泄漏。
用函数声明定义命令构造器,明确操作契约
每个绘图操作(如画线、拖拽节点、缩放画布)都应是一个独立命令类。用函数声明而非 class,便于显式控制 this 绑定和原型继承:
- 函数内部只做必要初始化(如保存原始坐标、选中 ID、当前缩放值),不捕获大对象或 DOM 引用
- 所有命令共享同一套原型方法(
execute()、undo()、redo()),避免每个实例重复创建函数 - 例如:function MoveNodeCommand(nodeId, fromPos, toPos) { this.nodeId = nodeId; this.from = fromPos; this.to = toPos; }
在原型上集中实现通用行为,降低耦合
撤销重做不是简单倒序执行,需处理依赖、合并、边界校验。这些逻辑不应散落在各命令里:
MoveNodeCommand.prototype.undo = function() { updateNodePosition(this.nodeId, this.from); };- 添加
canMerge(other)原型方法,让连续移动同一节点的命令自动合并,减少栈深度 - 统一注入
getSnapshot()调用时机(如仅在 execute 后触发),避免重复序列化整个画布
用原型链实现命令工厂与策略分发
复杂软件中命令类型多(矢量编辑、图层操作、样式变更),靠 if-else 判断易失控。可用原型链构建轻量工厂:
- 定义基类
DrawCommand函数,挂载默认serialize()和validate() - 各子命令(
ResizeGroupCommand、ChangeLayerOrderCommand)通过Object.setPrototypeOf(child, DrawCommand.prototype)继承 - 状态机执行时,只认
command.execute(),无需 switch 类型——新增命令只需继承并覆盖必要方法
状态机本身保持极简,专注栈管理和生命周期
撤销重做栈不是命令容器,而是带约束的双端队列:
- 用数组 + 指针(
currentIndex)代替 push/pop,支持任意位置跳转 - 每次
execute()后,清空redoStack并将新命令推入undoStack;undo()时调用命令自身undo(),再移入redoStack - 关键细节:命令实例在 undo/redo 完成后立即解除对画布模型的强引用(如置 null 或用 WeakMap 存储临时状态),防止内存堆积











