Skip to content

Proxy 与 Object.defineProperty区别 #29

Open
@TieMuZhen

Description

@TieMuZhen

Proxy 与 Object.defineProperty的优劣

Object.definedProperty

缺点

  • 对新增的属性监听不到,无法实现响应式
  • 数组API方法无法监听到
  • 只能监听某个属性而不能监听整个对象。
  • defineproperty监听需要知道是哪个对象的哪个属性,而proxy只需要知道哪个对象就可以了。也就是会省去for in循环提高了效率。
  • 因为在原对象身上新增或修改属性增加描述符的方式实现的监听效果,一定会修改原数据。而proxy只是原对象的代理,proxy会返回一个代理对象不会在原对象上进行改动,对原数据无污染。

优点

  • 尽管 Object.defineProperty 有诸多缺陷,但是其兼容性要好于 Proxy.
// 拦截器
let obj = {};
let temp = 'Yvette';
Object.defineProperty(obj, 'name', {
    get() {
        console.log("读取成功");
        return temp
    },
    set(value) {
        console.log("设置成功");
        temp = value;
    }
});

obj.name = 'Chris';
console.log(obj.name);

注意: Object.defineProperty定义出来的属性,默认是不可枚举(enumerable: false),不可更改(writable: false),不可配置(configurable: false

Proxy

Proxy 会劫持整个对象,读取对象中的属性或者是修改属性值,那么就会被劫持。但是有点需要注意,复杂数据类型,监控的是引用地址,而不是值,如果引用地址没有改变,那么不会触发set。

let obj = {
    name: 'Yvette', 
    hobbits: ['travel', 'reading'], 
    info: {
        age: 20,
        job: 'engineer'
    }
};
let p = new Proxy(obj, {
    get(target, key) { //第三个参数是 proxy, 一般不使用
        console.log('读取成功');
        return Reflect.get(target, key);
    },
    set(target, key, value) {
        if(key === 'length') return true; //如果是数组长度的变化,返回。
        console.log('设置成功');
        return Reflect.set(target, key, value);
    }
});
p.name = 20; //设置成功
p.age = 20; //设置成功; 不需要事先定义此属性
p.hobbits.push('photography'); //读取成功;注意不会触发设置成功
p.info.age = 18; //读取成功;不会触发设置成功

最后,我们再看下对于数组的劫持,Object.definedProperty 和 Proxy 的差别

Object.definedProperty 可以将数组的索引作为属性进行劫持,但是仅支持直接对 arry[i] 进行操作,不支持数组的API,非常鸡肋。

let arry = []
Object.defineProperty(arry, '0', {
    get() {
        console.log("读取成功");
        return temp
    },
    set(value) {
        console.log("设置成功");
        temp = value;
    }
});

arry[0] = 10; //触发设置成功
arry.push(10); //不能被劫持

Proxy 可以监听到数组的变化,支持各种API。注意数组的变化触发get和set可能不止一次,如有需要,自行根据key值决定是否要进行处理。

let hobbits = ['travel', 'reading'];
let p = new Proxy(hobbits, {
    get(target, key) {
        console.log('读取成功');
        return Reflect.get(target, key);
    },
    set(target, key, value) {
        console.log('设置成功');
        return Reflect.set(target, key, value);
    }
});
p.splice(0,1) //触发get和set,可以被劫持
p.push('photography');//触发get和set
p.slice(1); //触发get;因为 slice 是不会修改原数组的

Proxy 相比于 defineProperty 的优势:

  • 基于 Proxy 和 Reflect ,可以原生监听数组,可以监听对象属性的添加和删除
  • 不需要深度遍历监听:判断当前 Reflect.get 的返回值是否为 Object ,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测
  • 只在 getter 时才对对象的下一层进行劫持(优化了性能)

所以,建议使用 Proxy 监测变量变化

改变原数组的9种方法

1、shift
2、unshift
3、pop
4、push
5、reverse
6、sort
7、splice(start,length,item)
start​
指定修改的开始位置(从0计数)。如果是负值,则表示从数组末位开始;如果负数的绝对值大于数组的长度,则表示开始位置为第0位。

length 可选
表示要移除的数组元素的个数。如果 length 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。

item 可选
要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。

8、copyWithin:方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。

[1, 2, 3, 4, 5].copyWithin(-2)
// [1, 2, 3, 1, 2]

[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

9、fill(填充数组元素的值,起始索引,终止索引)

const array1 = [1, 2, 3, 4];

console.log(array1.fill(0, 2, 4));
//  [1, 2, 0, 0]

console.log(array1.fill(5, 1));
// [1, 5, 5, 5]

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions