从Object.defineProperty到Vue的数据劫持

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值