用 proxy 实现属性修改历史记录的核心是拦截 set 操作:先用 reflect.get 获取旧值,再用 reflect.set 赋新值,最后将 {prop, oldvalue, newvalue, timestamp} 推入日志数组;需额外处理嵌套对象、数组方法、deleteproperty 及日志管理。

用 Proxy 实现属性修改的历史记录,核心是拦截 set 操作,并把每次变更(属性名、旧值、新值、时间戳)存入一个记录数组。关键在于:必须先读取原值(用 Reflect.get 或目标对象),再执行赋值(用 Reflect.set),最后记录变更。
创建带历史记录的代理对象
初始化时定义一个空数组存储日志,然后在 set 拦截器中捕获修改动作:
- 通过
Reflect.get(target, prop)获取当前属性值(注意处理 undefined 和未定义属性) - 调用
Reflect.set(target, prop, value)执行真实赋值 - 将变更信息(如
{ prop, oldValue, newValue, timestamp: Date.now() })推入日志数组 - 返回
true表示设置成功(否则会抛出 TypeError)
支持获取和回溯历史
可在代理对象上挂载方法或额外暴露日志访问接口:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 提供
getHistory()方法直接返回全部记录 - 实现
undo():弹出最新一条记录,用oldValue写回对应属性(需确保该属性可写且未被删除) - 添加
resetTo(index):按日志索引恢复到某次修改前的状态(需逐条重放或快照式保存)
注意边界情况
实际使用中容易忽略这些细节:
- 对象嵌套时,Proxy 默认不递归代理子对象——如需深层监听,得在
get拦截器里对返回的对象也套一层 Proxy - 数组索引赋值(如
arr[0] = x)会被set拦截,但push、splice等方法调用不会触发set,需额外拦截apply - 删除属性(
delete target.prop)走的是deleteProperty拦截器,要单独记录“删除”动作 - 避免日志无限增长,可加最大长度限制或提供
clearHistory()
简单可用示例
以下是一个最小可行封装:
const createTrackedObject = (obj) => {const history = [];
const handler = {
set(target, prop, value) {
const oldValue = Reflect.get(target, prop);
const success = Reflect.set(target, prop, value);
if (success) {
history.push({ prop, oldValue, newValue: value, timestamp: Date.now() });
}
return success;
},
get() { return Reflect.get(...arguments); }
};
const proxy = new Proxy(obj, handler);
proxy.__history__ = history;
return proxy;
};
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










