1、Proxy 对象到底是什么?
Proxy 对象到底是什么呢?Proxy 的意思是 代理,proxy对象的作用是:通过Proxy 创建1个代理对象,然后通过操作代理对象允许你对指定的对象的一些行为进行自定义处理。
Proxy(target,handler); Proxy构造函数接收2个对象,第1个参数就是要处理的对象,第2个参数就是要自定义处理的方法的合集(也就是个对象)。
很抽象?其实就和js中的Object.defineProperty很像(也就是访问器属性,vue2.x的底层就是用它来实现的)。
Object.defineProperty 定义访问器属性,可以对某个属性的读写行为进行控制,在Proxy中也可以做到,而且Proxy更灵活和强大,它能做到很多访问器属性做不到的事情。
比如,监听属性删除事件(delete obj.prop;),in 事件('id' in obj;), apply 调用等。
先来看看,proxy对象有哪些内容。
var targetObj = {
id : 1,
name : 'pxh',
age : 20,
school : '小学'
}
var handler = {};
// 此处,我们先不对targetObj的行为进行干预,所以传个空对象进去即可。
var proxy = new Proxy(targetObj,handler);
console.log(proxy);
看看打印的proxy是什么鬼,如下图。
可以看到,proxy对象中,包含了Handler属性和Target属性和IsRevoked,它们的值分别是我们传入的handler以及 targetObj和false。
这个isRevoked表示是否可撤销,生成可撤销的proxy对象用Proxy.revocable()方法,具体可去MDN查看文档。
2、通过Proxy 对象操作原对象
上面我们创建了1个proxy对象,现在我们尝试通过操作proxy对象来操作原对象,操作proxy对象就和操作原生对象一样即可。(其实是proxy对象内部做了映射。)
var targetObj = {
id : 1,
name : 'pxh',
age : 20,
school : '小学'
}
var handler = {};
// 此处,我们先不对targetObj的行为进行干预,所以传个空对象进去即可。
var proxy = new Proxy(targetObj,handler);
/**
* 1、读取及修改属性,可以看到原来的对象的属性也被修改了
*/
console.log(proxy.age); // 20
console.log(targetObj.age); // 20
proxy.age = 22;
console.log(proxy.age); // 22
console.log(targetObj.age); // 22
/**
* 2、删除proxy对象的属性,影响原来的对象的属性
*/
console.log(proxy.school); // 小学
console.log(targetObj.school); // 小学
delete proxy.age;
console.log(proxy.school); // undefined
console.log(targetObj.school); // undefined
3、set方法和get方法
好,现在我们可以开始干预原来对象的行为了,具体我们通过实现以下方法达到干预对象行为的目的。
- handler.apply
- handler.construct 干预构造函数的new 行为
- handler.defineProperty 干预对象的数据属性或者访问器属性定义
- handler.deleteProperty 干预对象的属性删除行为
- handler.get 干预对象的属性读取行为
- handler.getOwnProperty 干预对象的属性的特征值
- handler.has 干预对象的in行为(prop in obj)
- handler.isExtensible
- handler.ownKeys
- handler.set 干预对象的属性设置行为
- ...
先来干预get行为(属性读取行为)
var targetObj = {
id : 1,
name : 'pxh',
age : 20,
school : '小学'
}
var handler = {
// 定义get方法,get方法可以接收2个参数,分别是原来的对象及属性
get : function(target,prop){
console.log(`${prop}属性正在被查看`);
console.log(targetObj == target); // true
return target[prop];
}
};
var proxy = new Proxy(targetObj,handler);
console.log(proxy.id);
/**
* 可以看到,打印顺序为:
* id属性正在被查看
* true
* 1
*/
接下来把某些属性变为 “私有” ,如不允许读取id属性
定义set方法,不允许修改id,name,age属性
var targetObj = {
id : 1,
name : 'pxh',
age : 20,
school : '小学'
}
var handler = {
// 定义get方法,get方法可以接收2个参数,分别是原来的对象及属性
get : function(target,prop){
if(prop == 'id'){
return undefined;
}
return target[prop];
},
// 定义set方法,set方法比get多1个参数,那就是该属性修改时的值
set : function(target,prop,value){
if(prop == 'id' || prop == 'name' || prop == 'age'){
console.log(`不允许修改${prop}属性`)
}else{
target[prop] = value;
}
}
};
var proxy = new Proxy(targetObj,handler);
/**
* 修改属性,分别打印
* 不允许修改id属性
* 不允许修改name属性
* 不允许修改age属性
*/
proxy.id = 2;
proxy.name = 'pxh222';
proxy.age = 23;
proxy.school = '中学'; // 这个无打印
/**
* 读取属性,可以看到分别打印
* undefined
* pxh
* 20
* 中学 // 这个没有拦截,因此可以修改
*/
console.log(proxy.id);
console.log(proxy.name);
console.log(proxy.age);
console.log(proxy.school);
4、干预删除行为(对delete obj.prop语句生效)
同样的,我们对删除对象属性的行为进行干预,不允许删除id,name,age属性。
var targetObj = {
id : 1,
name : 'pxh',
age : 20,
school : '小学'
}
var handler = {
// 在handler中定义get方法,get方法可以接收2个参数,分别是原来的对象及属性
get : function(target,prop){
if(prop == 'id'){
return undefined;
}
return target[prop];
},
// set方法比get多1个参数,那就是该属性修改时的值
set : function(target,prop,value){
if(prop == 'id' || prop == 'name' || prop == 'age'){
console.log(`不允许修改${prop}属性`)
}else{
target[prop] = value;
}
},
/**
* 这个方法要求返回个boolean值,表示是否删除成功
* 如果返回的值不是boolean值,则会进行类型转换成boolean值再返回
*/
deleteProperty : function(target,prop){
if(prop == 'id' || prop == 'name' || prop == 'age'){
console.log(`不允许删除${prop}属性`);
return false;
}else{
delete target[prop];
return true;
}
}
};
var proxy = new Proxy(targetObj,handler);
/**
* 尝试删除id属性,可以看到打印顺序为:
* 不允许删除id属性
* false
*/
console.log(delete proxy.id);
/**
* 删除school属性,可以看到打印
* true
* undefined
*/
console.log(delete proxy.school);
console.log(proxy.school);
5、干预prop in obj行为(判断对象是否有某个属性)
上面我们不允许获取对象的id值,也不可以修改和删除,现在我们把它隐藏掉。
var targetObj = {
id : 1,
name : 'pxh',
age : 20,
school : '小学'
}
var handler = {
// 在handler中定义get方法,get方法可以接收2个参数,分别是原来的对象及属性
get : function(target,prop){
if(prop == 'id'){
return undefined;
}
return target[prop];
},
// set方法比get多1个参数,那就是该属性修改时的值
set : function(target,prop,value){
if(prop == 'id' || prop == 'name' || prop == 'age'){
console.log(`不允许修改${prop}属性`)
}else{
target[prop] = value;
}
},
/**
* 这个方法要求返回个boolean值,表示是否删除成功
* 如果返回的值不是boolean值,则会进行类型转换成boolean值再返回
*/
deleteProperty : function(target,prop){
if(prop == 'id' || prop == 'name' || prop == 'age'){
console.log(`不允许删除${prop}属性`);
return false;
}else{
delete target[prop];
return true;
}
},
/**
* 通过has 方法来控制,返回值也是个boolean,表示对象是否拥有某个属性
* 如果返回的值不是boolean值,则会进行类型转换成boolean值再返回
*/
has : function(target,prop){
if(prop == 'id'){
return false
}else{
return prop in target;
}
}
};
var proxy = new Proxy(targetObj,handler);
console.log('id' in proxy); // false
console.log('name' in proxy); // true
6、总结
同样的,proxy还能干预对象的行为还有很多,这里就不一一介绍了。感兴趣戳MDN文档