proxy 通过 defineproperty trap 拦截 object.defineproperty 调用,控制属性描述符修改(如 writable),配合 set trap 实现完整写权限管控;示例中限制 id、createdat 等关键属性不可重定义为可写或可配置,并默认新属性只读。

Proxy 本身不提供 defineProperty 拦截器,但可以通过 defineProperty 这个 trap 拦截对目标对象调用 Object.defineProperty() 或 Object.defineProperties() 的行为,从而控制属性是否能被定义、修改描述符(如 writable、configurable、enumerable)——这正是限制“属性修改权限”的关键入口之一。
用 defineProperty trap 控制属性可写性
想让某个属性不可被后续修改(比如设为只读),不能只靠拦截 set;因为开发者可能通过 Object.defineProperty(obj, 'x', { writable: false }) 主动降级权限。此时需在 defineProperty 阶段就拒绝危险操作:
- 检查传入的 descriptor 是否试图将
writable: true改为false,或反之; - 对已存在的关键属性(如
'id'),直接返回false拒绝重定义; - 允许新增非敏感属性,但强制加上
writable: false等安全默认值。
配合 set trap 实现完整写权限管控
defineProperty 和 set 是互补的:前者管“定义/改描述符”,后者管“赋值”。例如:
- 若你希望
user.name只读,既要拦截set返回false(严格模式下报错),也要拦截defineProperty,防止有人绕过set直接改writable; - 可在
defineProperty中统一校验:对已有属性,只允许保持原writable值;对新属性,按白名单决定是否允许写; - 注意:如果目标属性本身是
configurable: false,后续任何defineProperty修改其writable都会失败——这是底层机制,Proxy 的 trap 仍会被触发,但返回true并不保证成功,需结合Reflect.defineProperty的返回值判断。
实际可运行示例
以下 Proxy 限制所有已有属性不可被重新定义为可写,并阻止删除关键字段:
const protectedKeys = new Set(['id', 'createdAt']);
const target = { id: 1, name: 'Alice', createdAt: Date.now() };
const proxy = new Proxy(target, {
defineProperty(obj, prop, descriptor) {
if (prop in obj && protectedKeys.has(prop)) {
if (descriptor.writable === true || descriptor.configurable === true) {
console.warn(`禁止修改 ${prop} 的 writable/configurable`);
return false;
}
}
// 允许新增非保护字段,但默认只读
if (!(prop in obj) && !protectedKeys.has(prop)) {
descriptor.writable = false;
descriptor.configurable = false;
}
return Reflect.defineProperty(obj, prop, descriptor);
},
set(obj, prop, value) {
if (protectedKeys.has(prop)) return false; // 禁止赋值
obj[prop] = value;
return true;
}
});
此时 proxy.id = 2 失败,Object.defineProperty(proxy, 'id', { writable: true }) 也失败,而 proxy.age = 30 成功但 age 默认只读。
注意事项与边界情况
-
definePropertytrap 不会拦截obj.prop = value这种赋值(那是set的事),也不会拦截delete obj.prop(那是deleteProperty); - 若目标对象某属性已是
configurable: false,即使 trap 返回true,实际调用Reflect.defineProperty仍会失败;建议始终用Reflect.defineProperty并检查其返回值; - 不要在 trap 中直接修改
descriptor后不传给Reflect调用——这会导致行为不一致,应始终委托给Reflect方法完成实际操作。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











