天天看點

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的兩個方法了。除了上面分享的這幾個方法外,還有很多攔截方法,感興趣的小夥伴可以結合上篇文章的整理自行探索一下。

喜歡的小夥伴歡迎點贊留言加關注哦!

繼續閱讀