Object.defineProperty和Proxy的比较

Object.defineProperty和Proxy的比较

一.Object.defineProperty 介绍

1.基本用法

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性

Object.defineProperty(obj, prop, desc);
//obj 需要定义属性的当前对象
//prop 当前需要定义的属性名
//desc 属性描述符

通过Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,分别为数据描述符,存取描述符,下面分别描述下两者的区别:
1.数据描述符 --特有的两个属性(value,writable):

const Obj = {}
Object.defineProperty(Obj, 'age', {
   val: 18,
   writable: false ,// 是否可以改变,默认false不可以改变
   enumerable:true ,//是否可枚举
   configrable:true//是否可配置
});
Obj.age=20;
console.log(Obj.age)//undefined,因为writable不能改变

2.存取描述符 (是由 getter、setter 函数功能来描述的属性)

Object.defineProperty(data, key, {
                enumerable: true,
                get() {
                    console.log('劫持了');
                    return val;
                },
                set(newVal) {
                    if (newVal === val) {
                        return;
                    }
                    val = newVal;
                }
            });

2.在vue2.x中的应用

有很多人说Object.defineProperty不能监听数组的变化,这种说法是不严谨的,准确的说是不能监听到数组某些方法的变化。

function Observer(data) {
        for (let key in data) {
            let val = data[key];
            Object.defineProperty(data, key, {
                enumerable: true,
                get() {
                    console.log('数组劫持了');
                    return val;
                },
                set(newVal) {
                    if (newVal === val) {
                        return;
                    }
                    val = newVal;
                }
            });
        }
  }
let arr=['张','王','李','赵']
Observer(arr);
arr.pop()//会触发get函数,控制台会打印「数组劫持了」
arr.push('周');//不会触发,劫持失败

总结:数组的[‘push’, ‘unshift’, ‘splice’, ‘reverse’, ‘sort’, ‘shift’, ‘pop’]这些方法中splice、‘push’、unshift是劫持不到的,在vue2.0源码中,是对数组的这三个方法进行了重写。具体如下:

let arrayProto = Array.prototype;
let newProto = Object.create(arrayProto); 
const arrMethods=['push', 'unshift', 'splice', 'reverse', 'sort', 'shift', 'pop'];
arrMethods.forEach(method => {
    newProto[method] = function(...args) {
        let inserted = null;
        switch (method){
            // args 是一个数组
            case 'push':
                inserted = args;
                break;
            case 'unshift':
                inserted = args;
                break;
            case 'splice': //splice方法有三个参数,第三个参数才是被新增的元素
                inserted = args.slice(2);//slice返回一个新的数组
                break;
        }
        if (inserted) ArrayObserver(inserted);      //因为inserted是一个新的数组项,所以要对数组的新增项重新进行劫持
        arrayProto[method].call(this, ...args);     //调用数组本身对应的方法
    }
})
function observer(obj){
...
    //通过Object.defineProperty进行循环递归绑定
}
function ArrayObserver(obj){ //对数组的新增项进行监控
    obj.forEach(item => {
        observer(item);  
    });
}

二.Proxy 介绍

基本用法

语法:

let p = new Proxy(target, handler);

1.target :需要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
2.handler: 一个对象,其属性是当执行一个操作时定义代理的行为的函数(可以理解为某种触发器)。具体的handler相关函数请查阅官网

let arr=['张','王','李','赵']
let testArr = new Proxy(arr, {
        get(target, key) {
            console.log('获取了getter属性');
            return target[key];
        }
});
testArr.push('周') //获取了getter属性

三.Object.defineProperty和Proxy对比

  1. Proxy性能优于Object.defineProperty。 Proxy代理的是整个对象,Object.defineProperty只代理对象上的某个属性,如果是多层嵌套的数据需要循环递归绑定;
  2. 对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到,需要借助$set方法;
  3. 数组的某些方法(push、unshift和splice)Object.defineProperty监听不到,Proxy可以监听到;
  4. Proxy在ie浏览器存在兼容性问题
上一篇:Proxy相比于defineProperty的优势


下一篇:vue2.0和vue3.0的区别