JavaScript实现撤销/恢复的历史命令模式:定义含execute/undo方法的Command基类,派生具体命令(如SetTextCommand)封装状态变更与快照,HistoryManager用history/redo栈管理命令执行、撤销与恢复。

用 JavaScript 实现支持撤销/恢复的历史命令模式,核心是把每个操作封装成可执行、可回滚的 命令对象,再用栈结构管理历史,通过面向对象方式解耦执行逻辑与控制逻辑。
定义命令接口:统一行为契约
先设计一个抽象的 Command 类(或构造函数),作为所有具体命令的基类。它规定每个命令必须有 execute() 和 undo() 方法:
class Command {
execute() {
throw new Error('子类必须实现 execute');
}
undo() {
throw new Error('子类必须实现 undo');
}
}
这样能确保所有命令行为一致,便于历史管理器统一调用,也利于类型检查和协作开发。
实现具体命令:封装状态变更与逆操作
每个实际操作(如添加文本、移动元素、修改颜色)都派生自 Command,在 execute 中做正向修改,在 undo 中精确还原——关键是要保存执行前的状态快照或必要参数,而不是依赖当前 DOM 或对象实时值。
立即学习“Java免费学习笔记(深入)”;
例如一个「设置元素文字」命令:
class SetTextCommand extends Command {
constructor(element, newText) {
super();
this.element = element;
this.newText = newText;
this.oldText = element.textContent; // 记录旧值,用于撤销
}
execute() {
this.element.textContent = this.newText;
}
undo() {
this.element.textContent = this.oldText;
}
}
注意:不推荐在 undo 里重新查 DOM 获取旧值,因为中间可能被其他操作改过;务必在 execute 前或构造时捕获关键状态。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
构建历史管理器:用栈存命令 + 控制指针
创建 HistoryManager 类,维护两个栈:history(已执行命令)和 redoStack(被撤销后暂存,供恢复);同时记录当前“游标位置”,避免误操作。
典型方法包括:
-
execute(command):调用command.execute(),压入history,清空redoStack -
undo():从history弹出最近命令,调用其undo(),压入redoStack -
redo():从redoStack弹出,调用execute(),压回history -
canUndo() / canRedo():返回布尔值,供 UI 控件判断是否启用按钮
示例简版实现:
class HistoryManager {
constructor() {
this.history = [];
this.redoStack = [];
}
execute(command) {
command.execute();
this.history.push(command);
this.redoStack = []; // 新操作使重做失效
}
undo() {
if (this.history.length === 0) return;
const cmd = this.history.pop();
cmd.undo();
this.redoStack.push(cmd);
}
redo() {
if (this.redoStack.length === 0) return;
const cmd = this.redoStack.pop();
cmd.execute();
this.history.push(cmd);
}
canUndo() { return this.history.length > 0; }
canRedo() { return this.redoStack.length > 0; }
}
使用示例:组合命令与管理器
实际使用时,创建管理器,生成具体命令,交由管理器调度:
const manager = new HistoryManager();
const el = document.getElementById('myDiv');
// 执行操作
manager.execute(new SetTextCommand(el, 'Hello'));
manager.execute(new SetTextCommand(el, 'World'));
// 撤销一次 → 变回 'Hello'
manager.undo();
// 恢复 → 变回 'World'
manager.redo();
如需支持复合操作(如“一键删除多个元素”),可实现 CompositeCommand,内部聚合多个子命令,统一执行或撤销——这也是面向对象组合优势的体现。
不复杂但容易忽略:命令对象应尽量轻量、无副作用,状态捕获要精准;历史栈不宜无限增长,可加最大长度限制或自动压缩合并(如连续输入用防抖合并为单个命令)。

















