Proxy实例常用的拦截操作方法整理

前言

小伙伴们大家好。前面一篇文章中我们对ES6中的Proxy进行了一个简单的分享。通过学习我们知道Proxy可以对对象进行拦截,从而可以根据业务需要做一些对应的逻辑处理。我们还知道Vue3.0对数据劫持做了一个很大的优化其中用到的就是Proxy。在文章的结尾我们还整理出了Proxy提供的支持拦截操作的一些实例方法。本章我们就抽取几个常用的拦截方法进行一个分享。

get(target, propKey, receiver)

看过上一篇文章的小伙伴应该都注意到了:在我们代码案例中用到最多的拦截方法就是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实例本身这一说法

set(target, key, value)

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(target, key)

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(target)

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(target, propKey)

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的两个方法了。除了上面分享的这几个方法外,还有很多拦截方法,感兴趣的小伙伴可以结合上篇文章的整理自行探索一下。

喜欢的小伙伴欢迎点赞留言加关注哦!

おすすめ

転載: blog.csdn.net/lixiaosenlin/article/details/120905464