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的时候触发监听事件。
缺陷总结:
- 无法检测到对象属性的添加或删除
- 无法检测数组元素的变化,需要进行数组方法的重写
- 无法检测数组的长度的修改
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