作用
- 都可以在JS中对对象属性的拦截和代理
区别
-
使用语法和方式
- Object.defineProperty
- ES5引入的方法
- 对单个属性进行代理
- 直接在对象上定义一个新属性,或修改对象现有属性,并返回该对象
const obj = {}; Object.defineProperty(obj, 'property', { value: 1, writable: true, enumerable: true, configurable: true }); console.log(obj.property); // 输出: 1 - Proxy
- ES6新特性
- 对整个对象进行代理
- 创建一个对象的代理
const target = {}; const handler = { get: function(target, property) { return `Getting property: ${property}`; } }; const proxy = new Proxy(target, handler); console.log(proxy.name); // 输出: Getting property: name
- Object.defineProperty
-
拦截范围
- Object.defineProperty
- 只能拦截对象属性的 读取(get) 和 设置(set) 操作,无法直接拦截其他操作(如删除属性、
in操作符、遍历属性等)。 - 对于数组的索引修改或方法调用(如
push、pop)也无法直接监听,需额外处理。 - 监听对象属性的变化,需要对每个属性单独使用
Object.defineProperty进行定义
const obj = {}; let value = 0; Object.defineProperty(obj, 'count', { get: function() { return value; }, set: function(newValue) { value = newValue; console.log('Count updated:', value); }, }); obj.count = 1; // 输出: Count updated: 1 - 只能拦截对象属性的 读取(get) 和 设置(set) 操作,无法直接拦截其他操作(如删除属性、
- Proxy
- 提供了 13 种拦截操作(如
get、set、deleteProperty、has、ownKeys等),可拦截更广泛的操作,包括属性删除、in操作符、遍历属性、数组索引修改等 - 无需为每个属性单独设置
const target = { name: 'John' }; const handler = { get: function(target, property) { return `Intercepted get: ${target[property]}`; }, set: function(target, property, value) { target[property] = value; console.log(`Intercepted set: ${property} = ${value}`); return true; }, deleteProperty: (target, property) => { console.log('deleteProperty', target, property) }, }; const proxy = new Proxy(target, handler); console.log(proxy.name); // 输出: Intercepted get: John proxy.age = 30; // 输出: Intercepted set: age = 30 delete proxy.name; // 输出 deleteProperty {name: 'John', age: 30} name - 提供了 13 种拦截操作(如
- Object.defineProperty
-
对新增属性的处理
-
Object.defineProperty
- 需要预先为
每个属性定义getter/setter,无法自动监听新增属性(需手动调用Vue.set或重新遍历对象)。
- 需要预先为
-
Proxy
- 直接代理
整个对象,自动监听所有属性变化(包括新增和删除属性),无需额外操作。
- 直接代理
-
-
性能
- Object.defineProperty
- 初始化时需遍历所有属性进行劫持,性能开销随对象规模增大而增加。
- 但属性访问时性能较好(直接调用
getter/setter)。
- Proxy
- 初始化时无需遍历属性,性能更高效,尤其适合大规模对象。
- 但每次操作需通过代理层,可能略微增加运行时开销(现代引擎优化后影响较小)。
- Object.defineProperty
-
兼容性
-
Object.defineProperty
- 支持 ES5 及以上环境(IE9+),兼容性较好。
-
Proxy
- 需 ES6 环境(IE 不支持),兼容性较低,但现代浏览器和 Node.js 均已支持。
-
选择建议
- 若需
兼容旧环境或仅需简单劫持,使用Object.defineProperty。 - 若需全面拦截、处理动态属性或
复杂对象,优先选择Proxy。
总结
| 特性 | Object.defineProperty | Proxy |
|---|---|---|
| 拦截操作 | 仅 get/set | 13 种拦截方法(如 delete、in) |
| 新增属性监听 | 需手动处理 | 自动监听 |
| 数组监听 | 需重写方法 | 直接拦截索引变化 |
| 初始化性能 | 需遍历属性,开销较大 | 无需遍历,高效 |
| 嵌套对象处理 | 递归劫持 | 按需代理 |
| 兼容性 | ES5+(广泛支持) | ES6+(现代环境) |
| 适用场景 | 简单属性劫持(如 Vue2) | 复杂响应式系统(如 Vue3) |