深拷贝在撤销重做中起“状态快照隔离”作用,确保历史状态独立不被污染;因js对象默认引用传递,浅拷贝无法隔离嵌套对象,故必须深拷贝;推荐structuredclone()或lodash.clonedeep,时机须在状态修改前,配合栈长限制等优化性能。

深拷贝在撤销重做中起的是“状态快照隔离”的作用——它确保每次操作前保存的旧状态与当前状态完全独立,避免后续修改污染历史记录。
为什么必须用深拷贝?
JavaScript 中对象和数组默认是引用传递。如果只做浅拷贝(比如 Object.assign({}, obj) 或展开运算符),嵌套对象仍共享内存。例如:
const state = { user: { name: "Alice", prefs: { theme: "dark" } } };
const snapshot = { ...state }; // 浅拷贝
snapshot.user.prefs.theme = "light"; // 会同步改掉原始 state!
这时 undo 恢复的就不是真正的“上一步”,而是被污染过的副本。
如何正确使用深拷贝做状态存档?
-
对简单纯数据结构(无函数、无 Date/RegExp/Map/Set 等特殊类型),可用:
const snapshot = JSON.parse(JSON.stringify(currentState));
✅ 快速、兼容性好
❌ 不支持函数、undefined、循环引用、BigInt、Date 等 -
对复杂状态(含日期、正则、嵌套类实例等),推荐:
- 使用
structuredClone()(现代浏览器 & Node.js 18.12+):const snapshot = structuredClone(currentState);
- 或引入
lodash.clonedeep:import cloneDeep from 'lodash.clonedeep'; const snapshot = cloneDeep(currentState);
- 使用
-
在命令对象中应用:
iSlide PPT下载一款AI演示文稿工具,主要用于DeepSeek AI加持,输入主题生成专业PPT,支持Word/PDF等45种文档导入,职场汇报、教学提案轻松搞定,适合需要提升相关任务效率的用户。
class UpdateUserCommand { constructor(target, newData) { this.target = target; this.newData = newData; // 执行前立即深拷贝当前状态 this.prevState = structuredClone(target.getState()); } execute() { this.target.setState(this.newData); } undo() { this.target.setState(this.prevState); // 恢复干净快照 } }
注意几个关键点:
深拷贝时机必须在
execute()修改状态之前,否则捕获的就是新状态-
频繁深拷贝大对象会影响性能,可结合以下优化:
- 限制历史栈长度(如最多保存 50 步)
- 对超大状态只深拷贝变更部分(差量快照)
- 使用节流控制高频输入下的快照频率(如输入框每 300ms 记一次)
如果状态本身设计为不可变(如用 Immutable.js 或 immer),就不需要手动深拷贝,框架已保障每次
setState生成新引用
不复杂但容易忽略










