Object.defineProperty和Proxy

Vue3.0放弃了Object.defineProperty,转而使用Proxy实现数据劫持,因为Proxy能更全面地监听对象和数组的变化。Object.defineProperty只针对单个属性,无法检测属性添加或删除,且数组变化处理不完善。而Proxy则直接代理整个对象,能精确捕获属性读取、设置、删除等操作,包括数组的元素变化和长度修改,提供更强大的响应式能力。

Vue3.0中,van You 放弃了Object.defineProperty,加入了Proxy来实现数据劫持,那么这两个函数有什么区别呢?本文深入的剖析一下两者的用法以及优缺点,相信看文本文你也会理解为什么Vue会选择Proxy。

认识 defineProperty 及其缺陷

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

// obj:要定义属性的对象。
// prop:要定义或修改的属性的名称。
// descriptor:要定义或修改的属性描述符。
Object.defineProperty(obj, prop, descriptor)

简单示例:

var user = {}
Object.defineProperty(user, 'name', {
  value: 'erikchan'
})

descriptor 属性描述是个对象,有以下属性:

属性名描述默认值
value属性的值undefine
configurable属性描述是否可修改
属性描述是否可以删除
false
enumerable对象是否可枚举false
writable对象是否可重写false
get访问改对象时, 调用此函数undefine
set属性值被修改时,调用此函数undefine
// get 和 set 劫持数据
Object.defineProperty(user, "name", {
    get: function(){
        console.log('get name')
        return initName
    },
    set: function(val){
        console.log('set name')
        initName = val
    }
});

Object.defineProperty能够通过 get 和 set 劫持对象的属性,但是需要对对象的每一个属性进行遍历劫持;如果对象上有新增的属性,则需要对新增的属性再次进行劫持;如果属性是对象,还需要深度遍历。这也是为什么Vue给对象新增属性需要通过$set的原因,其原理也是通过Object.defineProperty对新增的属性再次进行劫持。

看一下 Object.defineProperty 对数组的劫持:

var list = [1,2]

Object.defineProperty(list,  index, {
    get: function(){
        console.log("get index:" + index)
        return initName
    },
    set: function(val){
        console.log("set index:" + index);
        initName = val
    }
});

list[1] = 0; 			// set index:1
list.push(-1) 			// 无输出
list.length = 5 		
console.log(list[4]) 	// undefined

但是 Object.defineProperty 在劫持对象和数组存在以下缺陷:

  • 无法检测到对象属性的添加或删除

  • 无法检测数组元素的变化,需要进行数组方法的重写

  • 无法检测数组的长度的修改

认识 Proxy

Proxy 相对 defineProperty,不在局限某个属性,而是直接对整个对象进行代理.

Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

基本语法:

var proxy = new Proxy(target, handler);

实例:

var target = {}

var proxyObj = new Proxy(
    target,
    {
        get: function (target, propKey, receiver) {
            console.log(`getting ${propKey}!`);
            return Reflect.get(target, propKey, receiver);
        },
        set: function (target, propKey, value, receiver) {
            console.log(`setting ${propKey}!`);
            return Reflect.set(target, propKey, value, receiver);
        },
        deleteProperty: function (target, propKey) {
            console.log(`delete ${propKey}!`);
            delete target[propKey];
            return true;
        }
    }
);

可以看到Proxy直接代理了target整个对象,并且返回了一个新的对象,通过监听代理对象上属性的变化来获取目标对象属性的变化, 并且 deleteProperty 还能监听属性的删除。

proxy 处理数组的劫持:

var list = [1,2]
var proxyObj = new Proxy(list, {
    get: function (target, propKey, receiver) {
        console.log(`getting ${propKey}!`);
        return Reflect.get(target, propKey, receiver);
    },
    set: function (target, propKey, value, receiver) {
        console.log(`setting ${propKey}:${value}!`);
        return Reflect.set(target, propKey, value, receiver);
    },
})

proxyObj[1] = 0; 		// setting 1:0
proxyObj.push(-1) 		// getting push!  getting length! setting 2:-1! setting length:3!
proxyObj.length = 5 	//setting length:5!

从上可以看出, 不管是数组下标或者数组长度的变化,还是通过函数调用,Proxy都能很好的监听到变化。

<think> 嗯,用户想了解Object.definePropertyProxy的区别,这个问题在前端开发中很常见。用户可能正在学习Vue.js的响应式原理,或者需要为项目选择合适的数据劫持方案。从用户直接提问的方式看,ta更关注技术细节而非基础概念。 引用[3]提供了很好的对比框架,包括拦截维度、数组处理、新增属性支持性能四个方面。不过需要补充实际代码示例帮助理解。用户提供的几个引用中,[1][2]侧重API用法,[3][4]侧重对比分析,正好可以结合起来回答。 Object.defineProperty的局限性主要体现在:需要遍历对象所有属性,对数组变异方法需要重写,无法检测属性增减。而Proxy的优势在于:直接代理整个对象,支持13种拦截操作,完美处理数组变化。不过要注意Proxy的兼容性问题,在IE11及以下无法使用。 考虑到用户可能是中级开发者,回答时应该:1)用对比表格直观展示核心差异;2)针对Vue响应式原理说明实际应用场景;3)给出可运行的代码片段。特别要强调Proxy的handler方法列表是个关键点,比如has/deleteProperty这些Object.defineProperty无法实现的能力。 用户可能隐含的需求是想知道为什么Vue3要改用Proxy,所以需要补充性能对比:Proxy在初始化时更快,但调用拦截器略慢;Object.defineProperty在初始化时递归遍历消耗更大,但触发setter更快。对于大型对象,Proxy的整体性能更好。 </think> 以下是 `Object.defineProperty` `Proxy` 的核心区别对比及详解(基于引用内容整理): --- ### **核心区别对比表** | **特性** | **Object.defineProperty** | **Proxy** | |-----------------------|----------------------------------------|-------------------------------| | **拦截维度** | **属性级别**(需逐个定义属性) | **对象级别**(代理整个对象) | | **新增属性监听** | 需手动重新调用 | **自动拦截** | | **数组处理** | 需重写数组方法(如 `push`) | **原生支持**数组操作拦截 | | **ES版本** | ES5 | ES6 | | **性能** | 较好(但初始化递归遍历消耗大) | 更优(惰性拦截,无初始化遍历)| --- ### **详细解析** #### 1. **拦截粒度不同** - **`Object.defineProperty`** 只能拦截**已定义属性**的读写操作,需显式指定每个属性(代码示例[^2][^3]): ```javascript const obj = {}; Object.defineProperty(obj, 'name', { get() { return this._name; }, set(val) { this._name = val; console.log('更新DOM'); } }); obj.name = 'Alice'; // 触发set -> 更新DOM ``` - ❌ **无法拦截**未预先定义的属性或动态新增属性。 - **`Proxy`** 代理**整个对象**,可拦截**13种操作**(包括属性增删、数组操作等): ```javascript const obj = { name: 'Bob' }; const proxy = new Proxy(obj, { get(target, key) { return target[key]; }, set(target, key, value) { target[key] = value; console.log('更新DOM'); return true; } }); proxy.age = 30; // 自动触发set -> 更新DOM(无需预先定义age) ``` --- #### 2. **数组处理能力** - **`Object.defineProperty`** 监听数组下标变更需重写数组方法(如 `push`): ```javascript const arr = []; Object.defineProperty(arr, 'push', { value: function(...args) { console.log('数组变更'); return Array.prototype.push.apply(this, args); } }); arr.push(1); // 手动重写方法触发 ``` - **`Proxy`** **原生支持**数组操作拦截(无需重写): ```javascript const arr = []; const proxyArr = new Proxy(arr, { set(target, key, value) { console.log('数组变更'); return Reflect.set(target, key, value); } }); proxyArr.push(1); // 自动触发set ``` --- #### 3. **性能差异** - **`Object.defineProperty`** 初始化时需**递归遍历所有属性**,对大型对象性能消耗大。 - **`Proxy`** **惰性拦截**:仅在访问属性时触发,初始化更快(尤其适合复杂对象)[^3]。 --- ### **应用场景** | **场景** | 推荐方案 | 原因 | |---------------------------|----------------------|------------------------------| | **兼容IE低版本** | `Object.defineProperty` | Proxy 不支持 IE11 及以下 | | **动态属性监听/Vue3响应式** | `Proxy` | 自动拦截新增/删除属性 | | **精细化控制单个属性** | `Object.defineProperty` | 属性级拦截更直接 | --- ### **总结** - **`Object.defineProperty`**: 适合**兼容性要求高**或**精确控制已知属性**的场景。 - **`Proxy`**: 适合**动态属性监听**、**数组操作**及**现代浏览器环境**,简化代码并提升性能。 > 在Vue2中使用 `Object.defineProperty` 实现响应式,而Vue3全面转向 `Proxy`,核心原因是后者解决了动态属性增删数组操作的监听缺陷[^3][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值