JavaScript可通过属性描述符配合Object.defineProperty实现属性访问审计,核心是用get/set存取器替代数据属性,将真实值存于WeakMap等私有位置,并在存取时记录日志、校验或拦截;支持单属性显式定义或批量封装函数,但仅限自有属性,不覆盖原型链,且需注意enumerable和writable行为变化。

JavaScript 中可以通过属性描述符(Property Descriptor)配合 Object.defineProperty 或 Object.defineProperties,在定义或重定义对象属性时插入访问控制逻辑,从而实现对属性读取(get)和写入(set)的审计。核心在于用存取描述符(accessor descriptor)替代数据描述符(data descriptor),将实际值存储于私有位置(如闭包变量、WeakMap 或 Symbol 属性),再于 get/set 中记录日志、校验、拦截等操作。
用 get/set 拦截属性访问并记录日志
最直接的方式是为特定属性显式定义存取器,在其中添加审计行为:
- 使用
Object.defineProperty将目标属性设为 accessor 类型(即提供get和set函数) - 真实值不直接挂载在对象上,而是存于外部变量或
WeakMap,避免递归触发自身存取器 - 每次访问都可打印时间、调用栈、操作类型(读/写)、旧值/新值等信息
例如:
const auditLog = [];
const privateValues = new WeakMap();
const obj = {};
Object.defineProperty(obj, 'name', {
get() {
const value = privateValues.get(this) ?? '(undefined)';
auditLog.push({ type: 'read', prop: 'name', value, time: Date.now() });
return value;
},
set(newValue) {
const oldValue = privateValues.get(this);
auditLog.push({ type: 'write', prop: 'name', oldValue, newValue, time: Date.now() });
privateValues.set(this, newValue);
}
});
批量审计多个属性:封装可复用的审计定义函数
手动为每个属性写 defineProperty 易出错且重复。可封装一个辅助函数,接收目标对象、属性名列表及审计策略:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
立即学习“Java免费学习笔记(深入)”;
- 支持传入自定义审计回调(如发送监控事件、触发告警)
- 自动处理属性是否已存在,避免覆盖已有可枚举性或配置性
- 保留原始属性特性(如
configurable、enumerable),仅替换存取逻辑
示例:
function auditProperties(target, props, auditCallback = console.log) {
props.forEach(prop => {
const desc = Object.getOwnPropertyDescriptor(target, prop);
const valueRef = new WeakMap();
Object.defineProperty(target, prop, {
configurable: desc?.configurable ?? true,
enumerable: desc?.enumerable ?? true,
get() {
const val = valueRef.get(this);
auditCallback('GET', prop, val, this);
return val;
},
set(val) {
const old = valueRef.get(this);
auditCallback('SET', prop, old, val, this);
valueRef.set(this, val);
}
});
});
}
结合 Proxy 实现更灵活的全局审计(补充说明)
属性描述符适合静态、明确的属性审计;若需动态拦截所有属性(包括未来新增的)、支持继承链审计或更细粒度控制(如拦截 in、delete、has 等),Proxy 是更自然的选择。但二者可共存:
- 属性描述符用于精确控制关键字段(如敏感配置项、状态标志)
- Proxy 用于兜底审计整个对象层级,或作为开发期调试工具
- 注意:Proxy 的 handler 方法(如
get)中若访问原始对象属性,可能绕过已定义的存取器,需谨慎设计委托逻辑
注意事项与限制
使用属性描述符做审计时需留意:
- 无法审计原型链上的属性访问——只作用于当前对象自身定义的属性
- 不可枚举属性(
enumerable: false)仍可通过get访问,但不会出现在for...in或Object.keys()中 - 若目标属性原本是数据属性(
value+writable),转为存取器后将丢失原writable行为,需自行在set中模拟 - 严格模式下,
set返回false会抛出 TypeError,建议在set中显式 throw 或返回true

















