es6 proxy 通过 get/set 拦截器实现属性实时校验与响应式,支持新增/删除属性、数组操作等,比 object.defineproperty 更全面;set 中先校验再 reflect.set 并触发更新,get 中收集依赖,深层对象需递归代理。

ES6 Proxy 实现对象属性的实时校验与数据响应式,核心在于用 get 和 set 拦截器控制读写行为,并配合 Reflect 保证操作安全、可预测。它比 Object.defineProperty 更全面——能监听新增/删除属性、数组方法、in 操作、遍历等,天然适合构建响应式系统和校验逻辑。
用 set 拦截器做实时属性校验
在 set 中对赋值前做类型、范围、格式等判断,不符合则拒绝或抛错,符合才交由 Reflect.set 执行真实赋值。
- 必须返回布尔值:
true表示设置成功,false或抛异常会阻止赋值(严格模式下会报错) - 校验逻辑可复用:比如统一检查字符串长度、数字是否在区间内、邮箱格式是否合法
- 示例:限制
age只能是 0–150 的整数
const user = {};
const proxy = new Proxy(user, {
set(target, key, value) {
if (key === 'age') {
if (!Number.isInteger(value) || value 150) {
console.warn('age 必须是 0–150 的整数');
return false; // 拒绝赋值
}
}
return Reflect.set(target, key, value); // 允许其他属性正常写入
}
});
proxy.age = 200; // 警告,不生效
proxy.age = 25; // 成功
用 get 拦截器触发依赖收集(响应式基础)
响应式不是“自动更新”,而是“谁用了我 → 记下来;我变了 → 通知它们”。get 是收集依赖的关键入口。
- 每次读取属性时,把当前正在执行的副作用函数(如渲染函数)存进该属性对应的依赖列表中
- 推荐用
Reflect.get(target, key, receiver),避免 this 绑定错误,尤其涉及原型链时 - 实际框架(如 Vue 3)会配合
track()函数完成依赖注册
let activeEffect = null;
const depsMap = new Map(); // { key → Set
const proxy = new Proxy(obj, {
get(target, key) {
if (activeEffect) {
if (!depsMap.has(key)) depsMap.set(key, new Set());
depsMap.get(key).add(activeEffect);
}
return Reflect.get(target, key);
},
set(target, key, value) {
const result = Reflect.set(target, key, value);
if (depsMap.has(key)) {
depsMap.get(key).forEach(effect => effect()); // 触发更新
}
return result;
}
});
校验 + 响应式可以同时存在
两者不冲突,反而互补:校验保障数据质量,响应式保障视图及时同步。关键是在 set 中先校验、再触发更新、最后执行赋值。
- 校验失败时,不调用
Reflect.set,也不触发更新,保持状态干净 - 校验通过后,
Reflect.set写入成功,紧接着通知依赖更新 - 如果需要异步校验(如接口验证),可在
set中返回 Promise,但注意:Proxy 的set必须同步返回布尔值,异步逻辑需另起机制(如包装成 reactive setter 方法)
注意数组和嵌套对象的处理
Proxy 默认只代理第一层,深层对象和数组需递归代理或特殊处理:
- 对数组,
push/pop等方法不会触发set,需在get中拦截这些方法并返回代理版本 - 对嵌套对象,
get返回值如果是对象,应自动用 Proxy 包装(即“懒代理”),避免手动递归 - 校验规则可分层定义:顶层字段规则 + 子对象 schema(类似 JSON Schema)











