双向数据绑定通过Object.defineProperty(Vue 2)或Proxy(Vue 3)劫持数据读写,配合依赖收集与派发更新实现数据与视图自动同步,并通过监听input等事件将用户操作映射回数据更新。

JavaScript 实现双向数据绑定,核心在于建立数据与视图之间的自动同步关系:当数据变化时,视图自动更新;当用户操作视图(如输入框内容改变),数据也同步更新。这并非 JavaScript 原生能力,而是通过语言特性(如 Object.defineProperty、Proxy)配合事件监听与模板解析等机制模拟出来的。
基于 Object.defineProperty 的实现(Vue 2 核心原理)
该方法可劫持对象属性的读取(get)和设置(set)操作,在其中插入响应式逻辑:
-
数据劫持:遍历对象所有属性,用
defineProperty重定义其get和set。在get中收集依赖(如当前正在渲染的 DOM 节点),在set中触发更新函数。 -
依赖收集:每个数据属性对应一个“依赖列表”(Dep),每当模板中访问该属性(例如
{{ message }}),就将当前的 Watcher(观察者,封装了更新 DOM 的逻辑)加入该列表。 -
派发更新:当属性被修改,
set触发,遍历依赖列表,调用每个 Watcher 的update()方法,进而重新渲染对应视图。
局限:无法监听新增/删除属性、数组索引赋值(如 arr[0] = 1)、或非对象类型(如字符串、数字原始值)。
基于 Proxy 的实现(Vue 3 及现代方案主流)
Proxy 是 ES6 提供的代理对象,能拦截整个对象的操作,比 defineProperty 更强大、更简洁:
立即学习“Java免费学习笔记(深入)”;
-
全面拦截:支持监听属性读取(
get)、设置(set)、删除(deleteProperty)、in操作、数组 length 修改、甚至 for...in 遍历等。 - 无需递归初始化:对嵌套对象,可在首次访问时懒创建 Proxy,避免 Vue 2 中对深层对象一次性全劫持的性能开销。
-
天然支持数组和 Map/Set:可直接拦截
push、pop等方法,无需额外重写原型方法。
注意:Proxy 不兼容 IE,需考虑浏览器支持场景。
视图到数据的绑定:如何响应用户输入?
仅监听数据变化不够,还需把 DOM 事件(如 input、change)映射回数据更新:
- 在编译模板时,识别
v-model或类似指令,为对应表单元素绑定input事件(textarea、input[type="text"])或change(checkbox、select)。 - 事件回调中,调用对应数据属性的 setter(如
obj.message = $event.target.value),从而触发上面所述的响应式更新链路。 - 同时,初始渲染时将数据值写入元素的
value或checked属性,保证“数据 → 视图”通路完整。
简易双向绑定示例(Proxy 版)
以下是一个极简但可运行的双向绑定示意:
const data = { message: 'Hello' };
const proxy = new Proxy(data, {
set(obj, key, newVal) {
obj[key] = newVal;
document.getElementById('msg').textContent = newVal;
document.getElementById('input').value = newVal;
return true;
}
});
// 初始化视图
document.getElementById('msg').textContent = data.message;
document.getElementById('input').value = data.message;
// 绑定输入事件
document.getElementById('input').addEventListener('input', e => {
proxy.message = e.target.value; // 触发 Proxy.set
});
实际框架(如 Vue、React(配合 useState + onChange))在此基础上封装了模板编译、依赖追踪、异步队列、组件隔离等能力,使开发体验更自然可靠。










