js 实现撤销重做
撤销重做的基本原理
撤销重做功能通常通过维护两个栈来实现:一个用于撤销(undoStack),一个用于重做(redoStack)。每次用户操作时,将当前状态保存到撤销栈中。执行撤销操作时,从撤销栈弹出状态并压入重做栈;执行重做操作时,从重做栈弹出状态并压入撤销栈。
实现步骤
定义状态管理类
创建一个类来管理状态和操作。初始化时设置空栈和当前状态。
class UndoRedoManager {
constructor(initialState) {
this.undoStack = [];
this.redoStack = [];
this.currentState = initialState;
}
}
添加状态更新方法
当用户执行操作时,调用此方法将新状态保存到撤销栈中,并清空重做栈。
updateState(newState) {
this.undoStack.push(this.currentState);
this.currentState = newState;
this.redoStack = [];
}
实现撤销功能
从撤销栈弹出最近的状态,保存到重做栈,并恢复为当前状态。

undo() {
if (this.undoStack.length === 0) return false;
this.redoStack.push(this.currentState);
this.currentState = this.undoStack.pop();
return true;
}
实现重做功能
从重做栈弹出最近的状态,保存到撤销栈,并恢复为当前状态。
redo() {
if (this.redoStack.length === 0) return false;
this.undoStack.push(this.currentState);
this.currentState = this.redoStack.pop();
return true;
}
完整代码示例
class UndoRedoManager {
constructor(initialState) {
this.undoStack = [];
this.redoStack = [];
this.currentState = initialState;
}
updateState(newState) {
this.undoStack.push(this.currentState);
this.currentState = newState;
this.redoStack = [];
}
undo() {
if (this.undoStack.length === 0) return false;
this.redoStack.push(this.currentState);
this.currentState = this.undoStack.pop();
return true;
}
redo() {
if (this.redoStack.length === 0) return false;
this.undoStack.push(this.currentState);
this.currentState = this.redoStack.pop();
return true;
}
}
使用示例
const manager = new UndoRedoManager({ text: '' });
manager.updateState({ text: 'Hello' });
console.log(manager.currentState); // { text: 'Hello' }
manager.updateState({ text: 'Hello World' });
console.log(manager.currentState); // { text: 'Hello World' }
manager.undo();
console.log(manager.currentState); // { text: 'Hello' }
manager.redo();
console.log(manager.currentState); // { text: 'Hello World' }
优化建议
对于大型应用或频繁状态更新的场景,可以考虑以下优化:
限制栈大小

防止内存无限增长,可以设置栈的最大长度。
constructor(initialState, maxStackSize = 100) {
this.maxStackSize = maxStackSize;
// ...其余初始化代码
}
updateState(newState) {
if (this.undoStack.length >= this.maxStackSize) {
this.undoStack.shift();
}
// ...其余代码
}
使用不可变数据
结合Immutable.js等库,可以更高效地管理状态变化和内存使用。
import { Map } from 'immutable';
const manager = new UndoRedoManager(Map({ text: '' }));
manager.updateState(Map({ text: 'Hello' }));
批量操作处理
对于连续操作,可以合并为单次撤销重做单元。
startBatch() {
this.batch = [];
}
endBatch() {
if (this.batch.length > 0) {
this.undoStack.push(this.batch);
this.redoStack = [];
}
this.batch = null;
}
updateStateInBatch(newState) {
if (this.batch) {
this.batch.push(this.currentState);
this.currentState = newState;
} else {
this.updateState(newState);
}
}






