闭包命令模式通过封装执行与还原逻辑实现撤回,每个命令捕获执行前状态快照并存于闭包内,配合history数组管理操作顺序,支持接收者解耦和幂等undo。

用闭包实现命令模式来支持撤回(Undo),关键在于把“执行动作”和“还原状态”都封装进同一个函数作用域里,让每次操作自带前后快照。这种方式轻量、无需类定义,特别适合中小型前端交互系统。
闭包命令对象必须携带状态快照
撤回不是简单倒放操作,而是回到执行前的状态。闭包天然能捕获执行前的上下文,比如 DOM 属性、变量值或 Canvas 状态。每个命令实例在创建时就应记录接收者当时的快照,供 undo() 调用。
- 执行前读取当前值(如
input.value、element.style.color、canvas.getContext('2d').getImageData(...)) - 把快照存在闭包内,不暴露给外部
- execute() 改变状态;undo() 恢复快照
命令栈与撤销逻辑靠数组管理
用一个数组(如 history = [])按顺序存放已执行的命令对象,每次点击“撤回”就 pop() 最后一个并调用其 undo() 方法。注意:新操作发生时要清空重做栈(如果支持重做)。
- 执行命令后:
history.push(command); redoStack = []; - 撤回时:
if (history.length) history.pop().undo(); - 避免重复执行:确保每个命令只被
execute()一次,且undo()可安全多次调用(幂等)
接收者与命令解耦靠参数注入
闭包命令不直接依赖具体 DOM 元素或业务对象,而是通过工厂函数接收——这样同一套命令逻辑可复用于不同输入框、画布或面板。
- 例如:
createCommand(inputElement)返回含execute和undo的对象 - 按钮点击绑定的是
command.execute,而非直接操作 DOM - 切换主题、调整字号、拖拽排序等场景,都可用同一模式组织
真实可用的最小化示例
以下是一个修改文本框内容并支持撤回的闭包命令实现:
const createTextCommand = (el) => {
let prevValue = el.value;
return {
execute: (newValue) => {
prevValue = el.value;
el.value = newValue;
},
undo: () => {
el.value = prevValue;
}
};
};
// 使用
const input = document.getElementById('my-input');
const cmd = createTextCommand(input);
const history = [];
document.getElementById('set-btn').onclick = () => {
cmd.execute('Hello World');
history.push(cmd);
};
document.getElementById('undo-btn').onclick = () => {
if (history.length) history.pop().undo();
};

















