JavaScript中Proxy可实现非侵入式对象属性监听与事件派发,通过set/get拦截器捕获读写操作,结合Map维护事件订阅中心,支持深度代理、异步回调及嵌套对象监听,避免Object.defineProperty局限性。

JavaScript 中用 Proxy 实现对象属性监听与事件派发,核心是拦截读写操作并触发自定义逻辑,不需要侵入原对象,也不依赖 Object.defineProperty 的局限性(比如无法监听新增/删除属性、数组索引赋值等)。
创建可监听的响应式代理对象
通过 Proxy 的 set 和 get 拦截器捕获属性访问与修改,并在内部维护一个事件订阅中心(如 Map 结构),按属性名存储对应的回调函数列表。
- 每次
set触发时,通知该属性所有监听者,并可额外派发全局变更事件 -
get拦截中可做依赖收集(类似 Vue2 的 getter 收集),但事件派发场景下通常只关注写操作 - 需注意避免无限递归:在
set中修改代理对象自身属性时,应使用Reflect.set(target, key, value)而非直接赋值
实现简易事件订阅-发布机制
为每个属性维护独立的事件队列,支持 on(key, callback) 订阅、off(key, callback) 取消、emit(key, value, oldValue) 派发。也可扩展支持通配符或全局 onAny。
- 用
WeakMap或普通Map存储代理实例与事件映射关系,避免内存泄漏 - 回调函数接收参数如
(newValue, oldValue, key),便于业务层判断变化 - 派发时可返回 Promise 数组,支持异步监听器等待(如保存日志、校验、UI 更新等)
处理嵌套对象与数组的深层监听
Proxy 默认只代理第一层,要实现深度响应,需在 get 拦截中对返回值做递归代理——但仅对对象/数组类型处理,避免代理原始值或函数。
- 检测返回值是否为 object 或 array,且非 null,再用同一套代理逻辑包装
- 对数组方法(如
push、pop)的拦截需重写其原型方法,或使用ownKeys+getOwnPropertyDescriptor做方法劫持 - 可配合
isProxy和Proxy.revocable实现安全解绑或调试控制
实际使用示例
以下是一个轻量级实现片段:
function createReactive(obj, events = new Map()) {
const handler = {
get(target, key) {
return Reflect.get(target, key);
},
set(target, key, value) {
const oldValue = target[key];
const result = Reflect.set(target, key, value);
// 派发事件
if (events.has(key)) {
events.get(key).forEach(cb => cb(value, oldValue, key));
}
return result;
}
};
return new Proxy(obj, handler);
}
// 使用
const state = createReactive({ count: 0 });
state.on = (key, cb) => {
if (!events.has(key)) events.set(key, []);
events.get(key).push(cb);
};
state.on('count', (v, old) => console.log(`count changed: ${old} → ${v}`));
state.count = 1; // 触发回调
不复杂但容易忽略细节:比如未处理 deleteProperty、未兼容 in 操作符、未考虑 Symbol 属性监听等。生产环境建议基于此思路封装成类,或直接使用成熟方案如 proxy-compat 或 Vue 3 的 reactive 底层逻辑作为参考。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











