闭包是实现轻量级双向绑定感的可靠方案,通过封装get/set函数、缓存值与监听器,支持校验、通知和差异判断,不侵入原始对象且兼容老旧环境。

要让原始 JavaScript 对象的某个属性具备“双向绑定感”——即外部修改属性值时能自动通知、内部赋值前可拦截校验、还能保留旧值做差异判断——闭包是轻量又可靠的选择。它不依赖 Proxy 或 defineProperty,适合低侵入、小范围、需兼容老环境的场景。
用闭包封装属性访问与变更逻辑
核心思路是:把属性的读写操作收归到一对函数中(get/set),用闭包保存当前值和监听回调,避免污染原始对象结构。
- 不改原始对象,只提供受控的访问入口
- 闭包内缓存最新值,set 时可对比新旧值,避免无效更新
- 支持注册多个监听器,属性变化时统一触发
实现一个带监听的闭包属性追踪器
下面是一个可直接运行的最小实现:
function createTrackedProp(initialValue) {
let value = initialValue;
const listeners = new Set();
return {
get() { return value; },
set(newValue) {
if (newValue !== value) {
const oldValue = value;
value = newValue;
listeners.forEach(fn => fn(newValue, oldValue));
}
},
watch(fn) { listeners.add(fn); },
unwatch(fn) { listeners.delete(fn); }
};
}
// 使用示例
const nameTracker = createTrackedProp("张三");
nameTracker.watch((newVal, oldVal) => {
console.log(`name 从 "${oldVal}" 变为 "${newVal}"`);
});
nameTracker.set("李四"); // 控制台输出:name 从 "张三" 变为 "李四"
这个 tracker 不动原始对象,也不依赖 this 或原型链,所有状态(value、listeners)都由闭包私有持有,天然防篡改。
如何让它“像 v-model 一样用”
可以进一步桥接到 DOM 元素,模拟简易双向效果:
- 监听 input 的 input 事件,调用 tracker.set()
- tracker.watch() 中同步更新另一个显示元素的 innerText 或 value
- 这样就形成:用户输 → tracker 改 → 显示变;代码改 tracker → 显示同步变
注意:这里不劫持对象属性本身,而是用函数调用替代点号访问,所以它适用于无法修改目标对象(如第三方库返回的对象)、或只想追踪个别关键字段的轻量需求。
和 Object.defineProperty / Proxy 的关键区别
闭包方案不是“响应式系统”,而是一个“受控代理”:
- 不自动收集模板依赖,需手动 watch
- 不支持 obj.prop 这种语法,必须走 .get()/.set()
- 但胜在零依赖、无兼容性问题、逻辑透明、调试友好
- 特别适合配置项管理、表单字段联动、状态快照比对等场景










