JavaScript中观察者模式与属性监听结合的核心是用Proxy精准响应数据变更:拦截set判断真实变化、以属性名为事件名订阅、Map管理多对多关系、Promise.all处理异步回调、batch支持批量更新、WeakMap防内存泄漏,并兼顾深层监听与调试友好性。

JavaScript中观察者模式与对象属性监听的结合,核心在于让数据变化自动触发订阅逻辑,而不是靠轮询或手动调用。关键不是“监听所有属性”,而是精准控制哪些变化值得通知、谁该收到、以及如何避免重复或内存泄漏。
用Proxy封装对象,统一拦截读写行为
原生Object.defineProperty只能监听已存在的属性,且对数组索引、新增属性无效;Proxy可代理整个对象,支持get/set/deleteProperty/has等操作,更适合构建响应式基础。
- 在set拦截器中判断值是否真正改变(避免相同值触发多次通知)
- 把属性名作为事件名,让观察者按需订阅特定字段,例如user.on('name', callback)
- 注意保留原始属性描述符(如writable、enumerable),否则会影响JSON.stringify等默认行为
观察者注册与通知解耦,支持多对多关系
一个属性可能被多个函数关注,一个函数也可能监听多个属性。用Map结构管理:Map
- 提供off()方法时,不仅要移除callback,还要在Set为空时清理对应key,防止内存泄漏
- 通知时用Promise.all()包裹异步回调,避免某个失败阻塞其余执行
- 可加一层队列机制,合并同一轮事件循环中的多次同属性变更,避免抖动
兼容非响应式场景:手动触发与批量更新
并非所有修改都来自赋值操作——比如后端返回整块新数据、或深层嵌套对象替换。需要暴露notify(key, newValue, oldValue)接口供主动调用。
立即学习“Java免费学习笔记(深入)”;
- 批量更新时用batch(() => { obj.a = 1; obj.b = 2; })暂停通知,结束后统一派发
- 深层属性监听(如user.profile.age)可用点路径字符串存储,解析后递归触发父级和子级通知
- 对Date、RegExp等特殊对象,建议转为JSON可序列化的格式再比较,避免引用相等误判
避免常见陷阱:循环依赖与调试友好性
当A的监听器修改B,而B的监听器又改回A,就容易陷入无限通知。应在通知前标记当前事务ID,拦截重复ID内的嵌套变更。
- 开发环境开启enableDebug(true),记录每次触发的属性、旧值、新值、调用栈
- 不直接暴露原始Proxy对象给外部,而是返回带on/off/notify方法的包装实例,控制访问边界
- WeakMap存储观察者,关联到目标对象本身,确保目标被回收时监听逻辑自动清理










