从Object.defineProperty到Vue的数据劫持

本文详细介绍了Vue 2.x中Object.defineProperty的使用及其限制,然后转向Vue 3.x的Proxy,对比两者在属性劫持和对象代理上的区别,重点讲解了如何避免数据劫持的问题,并讨论了Proxy在解决这些问题上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 数据劫持

​ Vue2.x Object.defineProperty

​ Vue3.x Proxy

Object.defineProperty()

Object.defineProperty(对象名, 属性名, 描述符)

基本用法

let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
});
console.log(person.name); // Ayasen

此时writable enumerable configurable三个属性默认为false。

且该三个属性不可以同时和set,get一起使用,因为通过value和通过set赋值上有功能重复。

let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
    writable: true,     //可否被重写——修改值
    enumerable: true,   //可否被for in或者Object.keys()枚举
    configurable: true, //可否重新通过Object.define或删除
});

writable

writable为false时不能重新给该属性赋值

let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
    writable: true,     //可否被重写——修改值
    enumerable: true,   //可否被for in或者Object.keys()枚举
    configurable: true, //可否重新通过Object.define或删除
});
console.log(person.name); // Ayasen
person.name = 'dd';
console.log(person.name); // dd
let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
    writable: false,     //可否被重写——修改值
    enumerable: true,   //可否被for in或者Object.keys()枚举
    configurable: true, //可否重新通过Object.define或删除
});
console.log(person.name); // Ayasen
person.name = 'dd';
console.log(person.name); // Ayasen

enumerable

enumerable为false时不可以被for in或者Object.keys()枚举

let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
    writable: true,     //可否被重写——修改值
    enumerable: true,   //可否被for in或者Object.keys()枚举
    configurable: true, //可否重新通过Object.define或删除
});

console.log(Object.keys(person)); // ["name"]
for (let prop in person) {
    console.log(prop);  // name
}
let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
    writable: true,     //可否被重写——修改值
    enumerable: true,   //可否被for in或者Object.keys()枚举
    configurable: true, //可否重新通过Object.define或删除
});
console.log(Object.keys(person)); // []
for (let prop in person) {
    console.log(prop);
}

configurable

let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
    writable: true,     //可否被重写——修改值
    enumerable: true,   //可否被for in或者Object.keys()枚举
    configurable: true, //可否重新通过Object.define或删除
});

console.log(person); // {name: "Ayasen"}
delete person.name;
console.log(person); // {}

Object.defineProperty(person, 'name', {
    value: 'Gintoki',
})
console.log(person.name); // Gintoki
let person = {};
Object.defineProperty(person, 'name', {
    value: 'Ayasen',
    writable: true,     //可否被重写——修改值
    enumerable: true,   //可否被for in或者Object.keys()枚举
    configurable: false, //可否重新通过Object.define或删除
});

console.log(person); // {name: "Ayasen"}
delete person.name;
console.log(person); // {name: "Ayasen"}

Object.defineProperty(person, 'name', {
    value: 'Gintoki',
})
console.log(person.name); // strict模式下报错,非strict模式其实可以正常改成'Gintoki'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QafdEiFf-1625558769130)(/Users/v_chengweixi/Library/Application Support/typora-user-images/截屏2021-07-06 下午3.09.15.png)]

get和set

之所以要用newName来赋值,是因为如果在get方法中写成return person.name,此时就会调用一次get方法,会陷入无限调用get的死循环。

let person = {};
newName = '';
Object.defineProperty(person, 'name', {
    get() {
        console.log('get: ' + newName);
        return newName;
    },
    set(newValue) {
        console.log('set: ' + newValue);
        newName = newValue;
    }
});

person.name = 'Ayasen'; // set: Ayasen
person.name; 						// get: Ayasne
缺陷

Object.defineProperty劫持对象需要对对象的每一个属性遍历,数组则要比那里每一个index。如果对象上有新增的属性,这要对新属性再次劫持,如果属性是对象,还需要深度遍历。

数组

监听数组时,同样无法监听新元素,比如数组中声明了3个元素,通过arr[4] = xxx这种方式赋值不会出发监听事件

Vue的解决方案时监听数组的"push", “pop”, “shift”, “unshift”, “splice”, “sort”, "reverse"七个方法。

无法在arr.length=x的时候触发监听事件。

缺陷总结:

  1. 无法检测到对象属性的添加或删除
  2. 无法检测数组元素的变化,需要进行数组方法的重写
  3. 无法检测数组的长度的修改
Proxy

​ 不再是对属性劫持,而是对整个对象进行代理。

let target = {};
let proxy = new Proxy(target, {
    get(target, p, receiver){
        console.log('get ' + p + ':' + target[p]);
        return Reflect.get(target, p, receiver);
    },
    set(target, p, value, receiver) {
        console.log('set ' + p + ':' + value);
        return Reflect.set(target, p, value, receiver);
    },
    deleteProperty(target, p) {
        console.log('delete: ' + p);
        delete target[p];
        return true;
    }
});

proxy.count = 1;  // set count:1
proxy.count;		  // get count:1
delete proxy.count;  // delete: count

参考:https://juejin.cn/post/6914109870585151496

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值