js 实现撤销
撤销功能的实现思路
在JavaScript中实现撤销功能通常需要记录操作历史,并在用户触发撤销时回退到之前的状态。以下是几种常见实现方式:
命令模式实现
使用命令模式封装操作,保存操作历史栈:

class CommandManager {
constructor() {
this.history = [];
this.redoStack = [];
}
execute(command) {
command.execute();
this.history.push(command);
this.redoStack = [];
}
undo() {
const command = this.history.pop();
if (command) {
command.undo();
this.redoStack.push(command);
}
}
redo() {
const command = this.redoStack.pop();
if (command) {
command.execute();
this.history.push(command);
}
}
}
状态快照实现
保存应用状态的完整快照:
class UndoManager {
constructor(initialState) {
this.history = [initialState];
this.currentIndex = 0;
}
get currentState() {
return this.history[this.currentIndex];
}
commit(newState) {
this.history = this.history.slice(0, this.currentIndex + 1);
this.history.push(newState);
this.currentIndex++;
}
undo() {
if (this.currentIndex > 0) {
this.currentIndex--;
return this.currentState;
}
return null;
}
redo() {
if (this.currentIndex < this.history.length - 1) {
this.currentIndex++;
return this.currentState;
}
return null;
}
}
使用Immutable.js优化
对于复杂状态管理,使用不可变数据结构更高效:

import { List } from 'immutable';
class ImmutableUndo {
constructor(initialState) {
this.history = List([initialState]);
this.index = 0;
}
get current() {
return this.history.get(this.index);
}
commit(newState) {
this.history = this.history.slice(0, this.index + 1).push(newState);
this.index++;
}
undo() {
if (this.index > 0) {
this.index--;
return this.current;
}
return null;
}
redo() {
if (this.index < this.history.size - 1) {
this.index++;
return this.current;
}
return null;
}
}
实际应用示例
在文本编辑器中的实现:
class TextEditor {
constructor() {
this.text = '';
this.undoManager = new CommandManager();
}
insertText(text, position) {
const command = {
execute: () => {
this.text = this.text.slice(0, position) + text + this.text.slice(position);
},
undo: () => {
this.text = this.text.slice(0, position) + this.text.slice(position + text.length);
}
};
this.undoManager.execute(command);
}
undo() {
this.undoManager.undo();
}
redo() {
this.undoManager.redo();
}
}
性能优化建议
对于大型应用,考虑以下优化策略:
- 限制历史记录的最大长度
- 使用差异算法只存储状态变化而非完整状态
- 对大型对象使用结构化克隆或序列化
- 实现节流机制防止高频操作产生过多历史记录
浏览器内置API
现代浏览器支持document.execCommand('undo')和document.execCommand('redo'),但仅限于可编辑区域且功能有限,不推荐作为主要解决方案。






