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都能很好的监听到变化。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值