当前位置:首页 > JavaScript

js 实现撤销重做

2026-02-02 22:30:01JavaScript

撤销重做功能的实现思路

实现撤销重做功能的核心是使用命令模式结合历史记录栈。通过维护两个栈(撤销栈和重做栈),可以跟踪用户操作并支持回退或前进。

基础数据结构

需要两个栈来存储操作:

  • undoStack:存储已执行的操作,用于撤销。
  • redoStack:存储已撤销的操作,用于重做。
class HistoryManager {
  constructor() {
    this.undoStack = [];
    this.redoStack = [];
  }
}

执行操作

每次用户执行操作时,将操作封装为一个命令对象并存入undoStack,同时清空redoStack

js 实现撤销重做

execute(command) {
  command.execute();
  this.undoStack.push(command);
  this.redoStack = []; // 新操作会清除重做历史
}

撤销操作

undoStack弹出最近的操作,执行其撤销逻辑,并将操作存入redoStack

undo() {
  if (this.undoStack.length === 0) return;
  const command = this.undoStack.pop();
  command.undo();
  this.redoStack.push(command);
}

重做操作

redoStack弹出最近撤销的操作,重新执行并放回undoStack

js 实现撤销重做

redo() {
  if (this.redoStack.length === 0) return;
  const command = this.redoStack.pop();
  command.execute();
  this.undoStack.push(command);
}

命令对象示例

每个操作需要实现executeundo方法:

class TextChangeCommand {
  constructor(textField, oldText, newText) {
    this.textField = textField;
    this.oldText = oldText;
    this.newText = newText;
  }

  execute() {
    this.textField.value = this.newText;
  }

  undo() {
    this.textField.value = this.oldText;
  }
}

完整实现示例

class HistoryManager {
  constructor() {
    this.undoStack = [];
    this.redoStack = [];
  }

  execute(command) {
    command.execute();
    this.undoStack.push(command);
    this.redoStack = [];
  }

  undo() {
    if (this.undoStack.length === 0) return;
    const command = this.undoStack.pop();
    command.undo();
    this.redoStack.push(command);
  }

  redo() {
    if (this.redoStack.length === 0) return;
    const command = this.redoStack.pop();
    command.execute();
    this.undoStack.push(command);
  }
}

// 使用示例
const history = new HistoryManager();
const input = document.getElementById('text-input');

input.addEventListener('input', (e) => {
  const command = new TextChangeCommand(
    input,
    input._lastValue || '',
    e.target.value
  );
  input._lastValue = e.target.value;
  history.execute(command);
});

document.getElementById('undo-btn').addEventListener('click', () => history.undo());
document.getElementById('redo-btn').addEventListener('click', () => history.redo());

优化方向

对于频繁操作(如文本输入),可以使用防抖来合并连续操作:

let debounceTimer;
input.addEventListener('input', (e) => {
  clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => {
    const command = new TextChangeCommand(
      input,
      input._lastValue || '',
      e.target.value
    );
    input._lastValue = e.target.value;
    history.execute(command);
  }, 300);
});

状态快照方案

对于复杂状态,可以采用快照模式存储完整状态:

class StateSnapshotCommand {
  constructor(appState, newState) {
    this.appState = appState;
    this.prevState = {...appState};
    this.newState = newState;
  }

  execute() {
    Object.assign(this.appState, this.newState);
  }

  undo() {
    Object.assign(this.appState, this.prevState);
  }
}

注意事项

  • 内存管理:大量操作历史会占用内存,需设置栈大小限制
  • 性能考量:复杂操作的序列化/反序列化可能影响性能
  • 并发控制:确保操作执行和状态变更的原子性

标签: 重做js
分享给朋友:

相关文章

js实现倒计时

js实现倒计时

实现倒计时的基本方法 使用 JavaScript 实现倒计时功能可以通过 setInterval 或 setTimeout 结合日期计算来完成。以下是几种常见的实现方式: 使用 setInterv…

js实现动画

js实现动画

使用 CSS 动画与 JavaScript 控制 通过 JavaScript 动态添加或移除 CSS 类来触发动画。CSS 定义关键帧(@keyframes),JavaScript 通过 classL…

js实现图片放大缩小

js实现图片放大缩小

实现图片放大缩小的基础方法 使用CSS的transform: scale()属性可以快速实现图片的放大缩小效果。通过JavaScript动态修改该属性值: const img = document.…

js 实现vue

js 实现vue

Vue.js 的基本实现 在 JavaScript 中实现 Vue.js 的核心功能,可以通过数据绑定、响应式系统和虚拟 DOM 来实现。以下是实现 Vue.js 核心功能的简化版本。 数据响应式系…

js实现拷贝

js实现拷贝

实现文本拷贝 使用 document.execCommand 方法(已废弃但兼容性较好): function copyText(text) { const textarea = document…

js实现论坛

js实现论坛

实现论坛的基本功能 使用JavaScript实现一个论坛需要结合前端和后端技术。前端可以使用React、Vue或Angular等框架,后端可以选择Node.js配合Express或Koa框架。数据库可…