proxy 通过拦截 set 和 get 实现响应式监听,set 中触发更新通知,get 中收集依赖,需配合 weakmap 等机制存储订阅关系,但仅对已存在属性生效,不支持新增属性、原型链及基本类型,且需递归代理深层对象。

Proxy 可以通过拦截对象的属性读写操作,实现类似观察者模式的效果,但要注意它本身不是观察者模式,而是提供了一种响应式基础能力。
用 Proxy 拦截 set 和 get 实现响应式监听
核心思路是:用 Proxy 包裹目标对象,在 set 拦截器中触发更新通知,在 get 拦截器中收集依赖(如用于自动订阅)。
- set 拦截器:检测属性是否真正变化(避免重复触发),然后调用所有订阅该属性的回调
- get 拦截器:在读取属性时,把当前正在执行的副作用函数(如渲染函数)记录为该属性的订阅者
- 需要配合一个依赖收集机制(例如用 WeakMap 存储每个属性对应的回调集合)
简易可运行示例
下面是一个最小可行版本,支持单个属性变更通知:
const observe = (obj, callback) => {
const handlers = new Map(); // 属性名 → 回调列表
const proxy = new Proxy(obj, {
get(target, prop) {
// 读取时暂存当前回调(模拟依赖收集)
if (window.activeEffect) {
if (!handlers.has(prop)) handlers.set(prop, new Set());
handlers.get(prop).add(window.activeEffect);
}
return target[prop];
},
set(target, prop, value) {
const oldVal = target[prop];
if (oldVal !== value) {
target[prop] = value;
// 通知所有监听该属性的回调
const effects = handlers.get(prop);
if (effects) effects.forEach(fn => fn());
}
return true;
}
});
// 提供一个简易 effect 注册方式
const effect = (fn) => {
window.activeEffect = fn;
fn(); // 初次执行,触发 get 收集依赖
window.activeEffect = null;
};
return { proxy, effect };
};
// 使用
const data = { count: 0 };
const { proxy, effect } = observe(data);
effect(() => {
console.log('count changed:', proxy.count); // 初次输出 0
});
proxy.count++; // 输出 'count changed: 1'
proxy.count++; // 输出 'count changed: 2'
注意边界与限制
Proxy 的响应式有天然局限,实际使用需留意:
- 仅对**已存在的属性**或**被 Proxy 拦截的操作**生效;新增属性、删除属性、数组索引赋值等需显式处理(如用
Reflect.set或重写defineProperty) - 无法监听原型链上的属性变化,也不能直接代理基本类型(需包装成对象)
- 深层嵌套对象需递归代理,否则内层修改不会触发外层通知
- 性能敏感场景下,过度依赖 Proxy + 依赖收集可能带来额外开销
和标准观察者模式的区别
传统观察者模式(Observer)强调“被观察者维护订阅者列表 + 主动通知”,而 Proxy 更偏向“被动拦截 + 按需触发”。它不强制要求对象自身具备 subscribe/notify 方法,而是把响应逻辑抽离到代理层。因此更适合构建响应式系统底层,而非直接替代设计模式中的 Observer 类。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











