小伙伴们大家好。前面一篇文章中我们对ES6中的Proxy进行了一个简单的分享。通过学习我们知道Proxy可以对对象进行拦截,从而可以根据业务需要做一些对应的逻辑处理。我们还知道Vue3.0对数据劫持做了一个很大的优化其中用到的就是Proxy。在文章的结尾我们还整理出了Proxy提供的支持拦截操作的一些实例方法。本章我们就抽取几个常用的拦截方法进行一个分享。
看过上一篇文章的小伙伴应该都注意到了:在我们代码案例中用到最多的拦截方法就是get和set了。但并没有对这些方法进行一个详细的说明。
- get(target, propKey, receiver)方法用于拦截对象的属性读取,也就是说当我们通过Proxy的实例去访问对象的属性时会优先进入到get方法中。
- get方法接收三个参数:target:要拦截的目标对象,propKey:目标对象中的属性, receiver(可选):Proxy实例本身(严格的说应该是操作行为所针对的对象),第三个参数用到的场景很少,一般都是前两个参数比较多。
- get方法是可以支持继承的。将以一个小案例演示。
let obj = {name:'Yannis'}
let proxy = new Proxy(obj,{
get(target, key, receiver){
console.log(target);
console.log(key);
return 'Hello '+ target[key];
}
});
obj.name;
// {name:'Yannis'} target要拦截的对象
// name key:正在访问的对象的属性
// 'Hello Yannis' 当我们访问对象属性时,拦截器自动加了个Hello前缀
// get方法是可以支持继承
let proxy = new Proxy({},{
get(target,key){
return 'Hello Proxy'
}
});
let obj = Object.create(proxy);
obj.name;//'Hello Proxy'
obj.age;//'Hello Proxy'
//obj本身是没有这两个属性的,但由于继承了proxy,因此会走到proxy的get方法中,所以不管访问obj的什么属性,始终都会返回'Hello Proxy'
// get方法的第三个参数
let obj = {name:'Yannis'}
let proxy = new Proxy({},{
get(target, key, receiver){
return receiver;
}
});
proxy.name === proxy;//true
//这里我们在get中直接返回了第三个参数,当通过proxy实例访问对象属性时,发现对象的属性跟proxy的实例是相等的。这也印证了第三个参数是Proxy实例本身这一说法
Proxy的set方法也是比较常用的一个拦截方法。
- set方法主要是用来拦截对象属性的设置用的,即当我们通过Proxy实例给对象属性赋值时会进入到set拦截里。该方法返回一个布尔值
- set方法接收4个参数,target:拦截的目标对象,propKey:目标对象的属性,value:要给目标属性设置的新值,receiver(可选):Proxy实例本身
- set方法大多用于对属性的合法校验或者添加一些额外的处理逻辑。
//假如Person对象有个age属性,那么年龄应该是一个数字,并且不能太大也不能太小,当用户输入年龄的时候我们就可以用set方法来进行拦截校验
let person = {age: 1}
let proxy = new Proxy(person,{
set(target,key,value){
//只对age属性进行处理
if(key === 'age'){
if(!Number.isInteget(value)){
throw new TypeError('The age is not an integer')
}
if(value<=0 || value>150){
throw new RangeError('The age is incorrect')
}
}
target[key] = value;//将新值赋给target的key属性
return true;
}
})
person.age = 50;//true
person.age='Yannis';//报错
person.age = 200;//报错
has()方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
- has方法接收两个参数:target:目标对象,key:需要查询的属性名,返回值为布尔类型
- has方法拦截的是HasProperty操作而不是HasOwnProperty操作,也就是说has方法不判断一个属性是本身的属性还是继承来的属性
- has方法拦截对for…in循环是不生效的
// 隐藏对象的某些属性不被in发现,比如带下划线的属性
let obj = {
_a:'a',
_b:'b',
c:'c',
d:'d'
}
let proxy = new Proxy(obj,{
has(target,key){
if(key[0] === '_'){
return false
}
return key in target;
}
})
'_a' in proxy;//false
'_b' in proxy;//false
'c' in proxy;//true
'd' in proxy;//true
ownKeys方法跟has方法类似也是跟对象属性操作相关的,但是ownKeys是用来拦截获取对象属性的方法。它主要是拦截如下几个获取属性的方法:
- Object.getOwnPropertyNames(proxy)
- Object.getOwnPropertySymbols(proxy)
- Object.keys(proxy)
- for…in循环
- 该方法返回一个数组。数组的元素为对象所有自身属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
- ownKeys方法只接收一个参数target,要拦截的目标对象
另外需要注意的是:在使用Object.keys()方法是,有三类属性会被ownKeys方法自动过滤
- 目标对象上不存在的属性
- 属性名为Symbol类型的值
- 不可遍历的属性(enumerable为false)
//还是以隐藏对象的下划线属性为例
let obj= {
_a: 'a',
_b: 'b',
c: 'c',
d: 'd'
};
let proxy = new Proxy(obj, {
ownKeys (target) {
return Reflect.ownKeys(target).filter(key => key[0] !== '_');
}
});
for (let key of Object.keys(proxy)) {
console.log(key);
}
// c
// d
deleteProperty 方法用于拦截删除属性的操作,返回值为布尔类型。如果该方法抛出错误或者返回false,当前属性就无法被delete命令删除。
- 该方法接收两个参数target:目标对象和propKey:对象对应的属性名
//还是以对象的下划线属性为例
let obj= {
_a: 'a',
_b: 'b',
c: 'c',
d: 'd'
};
let proxy = new Proxy(obj, {
deleteProperty(target, key) {
if(key[0] === '_'){
throw new Error('Can not delete the private property')
}
delete target[key]
return true;
}
});
delete proxy['c'];// true
delete proxy['_a'];// Error: Can not delete the private property
关于Proxy实例一些常用的拦截方法就分享到这里了,个人认为用到最多的就是get和set的两个方法了。除了上面分享的这几个方法外,还有很多拦截方法,感兴趣的小伙伴可以结合上篇文章的整理自行探索一下。
喜欢的小伙伴欢迎点赞留言加关注哦!