浅拷贝可作为轻量级状态快照基础,适用于单层基本类型状态;用Object.assign或展开运算符保存快照,配合history栈实现undo,但需警惕引用类型共享问题。

JS 浅拷贝本身不能直接实现完整的历史记录或状态回滚,但它可以作为轻量级状态快照的基础——只要每次变更前保存一份当前对象的浅拷贝,就能在需要时快速还原到那个“上一时刻”的状态(前提是对象结构简单、无嵌套引用)。
什么时候适合用浅拷贝做状态回滚
适用于以下情况:
- 状态对象只有一层属性(如 const state = { count: 1, name: 'Alice' };)
- 所有属性都是基本类型(string / number / boolean / null / undefined)或不可变值(如 Date、RegExp 实例较少变动)
- 不关心深层嵌套对象(如 state.user.profile.avatar)是否被意外修改
- 对内存和性能敏感,不想引入深拷贝开销
用 Object.assign 或展开运算符保存快照
每次修改前,把当前状态浅拷贝进历史栈:
let currentState = { count: 0, theme: 'light' };
const history = [];
function saveSnapshot() {
history.push(Object.assign({}, currentState));
// 或:history.push({ ...currentState });
}
function undo() {
if (history.length > 0) {
currentState = history.pop(); // 恢复上一个快照
}
}
// 示例操作
saveSnapshot(); // 记录初始状态
currentState.count = 5;
saveSnapshot(); // 记录 count=5 的状态
currentState.theme = 'dark';
undo(); // → currentState 变回 { count: 5, theme: 'light' }
注意浅拷贝的陷阱:引用值仍共享
如果状态里包含数组、对象、函数等引用类型,浅拷贝只复制引用地址,不是真正隔离:
let currentState = { list: [1, 2], user: { id: 1 } };
saveSnapshot();
currentState.list.push(3); // ✅ 不影响快照中的 list 引用(但…)
currentState.user.name = 'Bob'; // ❌ 快照里的 user 也会看到 name='Bob'
// 因为:{ ...currentState } 中的 user 是同一个对象引用
解决办法:
- 避免在状态中直接存放可变引用对象;改用不可变更新(如用 map 返回新数组)
- 对关键引用字段手动深拷贝(如 list: [...currentState.list])
- 或改用 structuredClone(现代环境支持)做真正浅层但安全的克隆
一个更健壮的简易回滚工具(带限制与清理)
加个最大历史长度,防止内存泄漏:
class SimpleUndoManager {
constructor(maxHistory = 10) {
this.history = [];
this.currentState = {};
this.maxHistory = maxHistory;
}
set(state) {
this.currentState = { ...state };
}
snapshot() {
this.history.push({ ...this.currentState });
if (this.history.length > this.maxHistory) {
this.history.shift(); // 丢弃最早一条
}
}
undo() {
if (this.history.length > 0) {
this.currentState = this.history.pop();
return true;
}
return false;
}
}
// 使用
const undoer = new SimpleUndoManager(5);
undoer.set({ x: 10, y: 20 });
undoer.snapshot();
undoer.set({ x: 15, y: 20 });
undoer.undo(); // → 恢复 { x: 10, y: 20 }


















