Object.defineProperty 与 Proxy解析
以下是关于 Object.defineProperty 与 Proxy 的核心知识点解析、对比及使用场景说明:
1. Object.defineProperty
核心功能
- 定义或修改对象属性的特性:如
value
、writable
、enumerable
、configurable
。 - 数据劫持:通过
getter
和setter
监听属性的读取和修改。
基本语法
Object.defineProperty(obj, prop, {value: 1, // 属性值writable: true, // 是否可修改enumerable: true, // 是否可枚举(如出现在 for-in 循环中)configurable: true,// 是否可删除或重新配置// 存取描述符(与 value/writable 互斥)get() { return this._value; },set(newVal) { this._value = newVal; }
});
示例:实现简单数据响应式
const data = { count: 0 };Object.defineProperty(data, 'count', {get() {console.log('读取 count');return this._count;},set(newVal) {console.log('更新 count');this._count = newVal;}
});data.count = 10; // 输出: "更新 count"
console.log(data.count); // 输出: "读取 count" → 10
缺点
- 无法监听新增/删除属性:需手动调用
Vue.set
或Vue.delete
(Vue 2 的限制)。 - 数组监听需特殊处理:通过重写数组方法(如
push
、pop
)。 - 性能问题:需递归遍历对象所有属性。
2. Proxy
核心功能
- 代理整个对象:拦截对象的 13 种操作(如属性读取、赋值、删除、函数调用等)。
- 更强大的监听能力:支持监听新增属性、数组变化等。
基本语法
const proxy = new Proxy(target, {get(target, prop, receiver) { /* 拦截读取 */ },set(target, prop, value, receiver) { /* 拦截赋值 */ },deleteProperty(target, prop) { /* 拦截删除 */ },// 其他拦截器:has、apply、construct 等
});
示例:实现数据响应式
const data = { count: 0 };const proxy = new Proxy(data, {get(target, prop) {console.log(`读取 ${prop}`);return Reflect.get(target, prop);},set(target, prop, value) {console.log(`更新 ${prop} 为 ${value}`);return Reflect.set(target, prop, value);}
});proxy.count = 10; // 输出: "更新 count 为 10"
console.log(proxy.count); // 输出: "读取 count" → 10
优势
- 全面监听:支持属性新增、删除、数组方法(如
push
)等。 - 性能优化:无需初始化遍历对象属性。
- 非侵入性:直接代理对象,不修改原对象。
3. 核心对比
特性 | Object.defineProperty | Proxy |
---|---|---|
监听范围 | 只能监听已存在的属性 | 监听整个对象的所有操作 |
数组支持 | 需重写数组方法 | 直接监听数组变化(如 push ) |
性能 | 初始化时递归遍历属性,性能较低 | 按需拦截,性能更高 |
兼容性 | 支持 IE9+ | 支持现代浏览器(IE 不支持) |
使用复杂度 | 需手动处理新增/删除属性 | 自动处理所有操作 |
4. 应用场景
Object.defineProperty
- 兼容性要求高:需支持旧浏览器(如 IE)。
- 简单数据劫持:已知属性的监听场景(如 Vue 2 的响应式系统)。
Proxy
- 现代浏览器项目:如 Vue 3、React 新特性。
- 复杂监听需求:需监听属性新增、删除或数组变化。
- 库/框架开发:提供更灵活的数据劫持能力。
5. 进阶示例
Proxy 实现深度监听
function reactive(obj) {return new Proxy(obj, {get(target, prop) {const value = Reflect.get(target, prop);// 递归代理嵌套对象return typeof value === 'object' ? reactive(value) : value;},set(target, prop, value) {console.log(`更新 ${prop}`);return Reflect.set(target, prop, value);}});
}const data = reactive({ user: { name: 'Alice' } });
data.user.name = 'Bob'; // 输出: "更新 name"
6. 总结
- Object.defineProperty:适合已知属性的简单劫持,兼容性好,但功能有限。
- Proxy:功能强大,适合现代项目,能全面监听对象变化,是未来趋势。
- 实际选择:根据项目需求(兼容性、功能复杂度)决定使用哪种方案。