用数组实现undo栈本质是用数组+索引模拟栈行为,通过current指针管理当前状态,配合按需快照、结构复用与容量控制实现高效内存管理。

用数组实现 Undo 栈本质是维护一个操作历史的线性序列,配合指针(索引)控制当前状态位置;内存快照则是在关键节点保存数据副本,避免每次操作都深拷贝全部数据——关键是按需快照、复用结构、控制容量。
Undo 栈:用数组 + 索引模拟栈行为
不依赖 Stack 类,直接用普通数组 + 一个 current 指针(整数索引)管理“当前可见状态”:
- 初始化时:
history = [],current = -1(表示无有效状态) - 执行新操作后:
history.splice(current + 1)清除未来分支(即 Redo 历史),再push(新快照),最后current++ - Undo:若
current > 0,则current--,返回history[current] - Redo:若
current ,则 <code>current++,返回history[current]
这样既支持撤销/重做,又自动丢弃“分叉后未走的路径”,节省内存。
快照策略:轻量复制 + 结构复用
不是每次操作都 JSON.parse(JSON.stringify(data)) ——开销大且无法保留函数、Date、Map 等类型。推荐方式:
- 只深拷贝变动部分:例如编辑表格某一行,只克隆该行对象,其余行引用原数组项
-
用不可变更新模式:如用
[...arr.slice(0, i), newItem, ...arr.slice(i+1)]替代arr[i] = newItem -
对基础数据结构打平快照:如状态是 {user, config, list},可只存
{user, config, list: list.map(item => ({...item}))},跳过函数和 DOM 引用
内存控制:限制长度 + 智能压缩
防止 history 数组无限增长:
- 设定最大长度(如
MAX_HISTORY = 50),push 前检查:if (history.length >= MAX_HISTORY) history.shift() - 对连续相似操作合并快照:比如用户快速输入 10 个字符,可只保留首尾两帧,中间用“差异描述”代替完整快照(进阶可选)
- 在空闲时(如
requestIdleCallback)清理深层嵌套引用,或把较老快照转为更紧凑格式(如只存 diff)
简单示例:文本编辑器 Undo
假设编辑器状态是字符串 content:
- 每次输入后,生成新字符串(字符串本身不可变,天然适合):
history.push(content) - Undo 时
content = history[current],无需深拷贝 - 若内容极大(如 10MB 文本),改存“变更位置 + 新增/删除内容”,还原时动态 patch,而非存全量字符串
核心思路一致:数组存轨迹,索引控状态,快照讲策略,内存有节制。











