Object.defineProperty 仅能为对象的单个已有属性添加 getter/setter 拦截,适用于精确控制读写行为(如日志、校验),但无法拦截新增/删除属性、数组操作或嵌套对象,Vue 2 基于此实现响应式,Vue 3 则改用 Proxy 全面替代。

可以用 Object.defineProperty 为对象的**单个属性**添加 getter/setter 拦截器,但无法直接拦截对象的新增属性、删除属性、枚举等整体操作——那是 Proxy 的职责。重点在于:它适用于精确控制某个已有属性的读写行为。
为已有属性添加 getter/setter 拦截
这是最常用场景:在不改变属性值的前提下,插入自定义逻辑(如日志、校验、依赖收集)。
- 必须先确保目标属性存在(或用
configurable: true允许后续修改) - 调用
Object.defineProperty(obj, key, descriptor),其中 descriptor 至少含get或set - 若原属性是可写的,建议保留
writable: false(因为定义了 getter/setter 后,writable自动失效)
示例:
const obj = { name: 'Alice' };
Object.defineProperty(obj, 'name', {
get() {
console.log('读取 name');
return this._name || '';
},
set(val) {
console.log('设置 name 为:', val);
this._name = String(val).trim();
},
configurable: true // 允许后续重新定义
});
obj.name = ' Bob '; // 输出:设置 name 为: Bob
console.log(obj.name); // 输出:读取 name → 'Bob'
给新属性添加拦截(非覆盖原有值)
如果对象原本没有该属性,也能用 defineProperty 添加带拦截器的属性,但需注意默认不可枚举、不可配置、不可写。
- 显式设置
enumerable: true才能在for...in或Object.keys()中出现 -
configurable: true是修改/删除该属性定义的前提 - 不要同时设
value和get/set,会报错
示例(添加一个响应式计数器):
const state = {};
Object.defineProperty(state, 'count', {
get() { return this._count || 0; },
set(val) {
if (typeof val === 'number') {
this._count = val;
console.log('count 已更新为:', val);
}
},
enumerable: true,
configurable: true
});
注意事项与限制
Object.defineProperty 是静态、细粒度的,不是响应式系统本身。它有明确边界:
- 无法拦截
obj.newKey = value这类动态新增属性(除非提前为每个可能的 key 都定义好) - 不能监听数组索引赋值(如
arr[0] = 1)或数组长度变更 - 对嵌套对象无递归能力,需手动遍历处理每一层
- Vue 2 的响应式核心基于它,正因这些限制,Vue 3 改用
Proxy
替代方案提示:何时该用 Proxy
如果需要拦截整个对象的行为(如新增/删除属性、in 操作、遍历、构造函数调用),应直接使用 Proxy:
const handler = {
get(target, key) {
console.log('访问属性:', key);
return key in target ? target[key] : undefined;
},
set(target, key, value) {
console.log('设置属性:', key, '=', value);
target[key] = value;
return true;
}
};
const proxied = new Proxy({}, handler);
proxied.foo = 123; // 触发 set
console.log(proxied.foo); // 触发 get

















