js代理(Proxy) 和 反射(Reflection)


在實際開發中經常會遇到js拋出的錯誤,但是我們有沒有想過自己去接管js異常驗證,根據自己的需求拋出異常呢?原本也許不行,但是在es6出來后就可以做到了

一、代理(Proxy)

什么是‘代理’ 呢?代理:就是調用new 創建一個和目標(traget)對象一直的虛擬化對象,然該代理中就可以攔截JavaScript引擎內部目標的底層對象的操作;這些底層操作被攔截后會觸發響應特定操作的陷阱函數
來看個簡單的案例

	let tartget = {};
	let proxy = new Proxy(target,{});
	proxy.name = 'proxy';
	console.log(proxy.name); // proxy
	console.log(tartget .name); // proxy
	
	tartget .name = 'tartget';
	console.log(proxy.name); // target
	console.log(tartget .name); // target

如案例
proxy.name = 'proxy'; 將proxy賦值給proxy.name時,代理就會將該操作轉發給目標,執行name屬性的創建;然而他只是做轉發而不會存儲該屬性;
so他們之間存在一個相互引用;tartget .name設置一個新值后,proxy.name值也改變了;

二、反射(Reflect)

那反射又是什么呢?反射:它提供了一個Reflect對象API;該對像中的方法默認特性與底層的操作對應;而每一個代理陷阱會對應一個命名和參數都相同的Reflect方法(其實就是每個代理陷阱都會對應一個Reflect api接口供覆寫JavaScript底層操作)

映射關系如下表:

代理陷阱 覆寫的特性 默認特性
get 讀寫一個屬性值 Reflect.get()
set 寫入一個屬性 Reflect.set()
has in操作 Reflect.has()
deleteProperty delete操作符 Reflect.deleteProperty()
getAPrototypeof Object.getAPrototypeof () Reflect.getAPrototypeof ()
setAPrototypeof Object.setAPrototypeof () Reflect.setAPrototypeof ()
isExtensible Object.isExtensible() Reflect.isExtensible()
preventExtensions Object.preventExtensions() Reflect.preventExtensions()
getOwnPropertyDescriptor Object.getOwnPropertyDescriptor() Reflect.getOwnPropertyDescriptor()
defineaProperty Object.defineaProperty() Reflect.defineaProperty()
ownKeys Object.keys() 、 Object.getOwnPropertyNames()和 Object.getOwnPropertySysmbols() Reflect.ownKeys()
apply 調用一個函數 Reflect.apply()
construct 用new調用一個函數 Reflect.construct()

三、使用set陷阱驗證屬性

接下來使用set陷阱來驗證一下對象屬性賦值操作(如為對象新增屬性,要求必須賦值為int)

	let target = {
		name :'target'
	};
	let proxy = new Proxy(target,{
		set(trapTarget,key,value,receiver){
			//忽略不希望受到影響的已有屬性
			if(!trapTarget.hasOwnProperty(key)){
				if(isNaN(key)){
					throw new TypeError("屬性必須是數字喲,親!");
				}
			}
			// 添加屬性
			return Reflect.set(trapTarget,key,value,receiver);
		}
	});
	
	// 添加一個新屬性
	proxy.count = 1;
	console.log(proxy.count); // 1
	console.log(proxy.count); // 1
	
	// 由於目標已有name屬性,so 如上第一個if不成立(賦值成功)
	proxy.name= "proxy";
	console.log(proxy.name); // proxy
	console.log(proxy.name); // proxy
	
	// 新建一個屬性同時賦值一個非int 值,第一個if成立,第二個if驗證isNaN(key) = true 即拋出異常
	proxy.anotherName = "proxy";

案例中set(trapTarget,key,value,receiver) 這個set陷阱默認接收 四個參數

  • trapTarget 用於接收屬性(代理的目標)的對象
  • key 要寫入的屬性鍵(字符串或Symbol類型)
  • value 被寫入的屬性的值
  • receiver 操作發生的對象(通常是代理)

四、使用get 陷阱驗證對象結構

let target = {};
console.log(target.name); // undefined

在JavaScript中調用一個對象不存在的屬性不會報錯,反而使用undefined代替被讀取屬性的值

而喝多時候會帶來意想不到的bug,現在我們可以使用get陷阱來驗證該問題
依然看這個案例

	let proxy = new Proxy(target,{
		get(trapTarget,key,receiver){
			//忽略不希望受到影響的已有屬性
			if(!(key in receiver)){
				throw new TypeError("sorry 親! 你找的 "+key+" 屬性不存在。!")
			}
			// 添加屬性
			return Reflect.get(trapTarget,key,receiver);
		}
	});
	// 添加一個屬性,
	proxy.name= "proxy";
	console.log(proxy.name); // proxy

	// 讀取一個不存在的屬性 直接會拋出異常
	console.log(proxy.nme);

如上使用in操作判斷receiver中是否存在被讀取的屬性;如果沒有拋出異常

其中get(trapTarget,key,receiver) 參數

  • trapTarget 被讀取屬性源對象(代理的目標)
  • key 要讀取的屬性鍵(字符串或Symbol類型)
  • receiver 操作發生的對象(通常是代理)

五、函數代理apply和construct陷阱

使用這個兩個陷阱來驗證函數調用時的參數的正確性
如下案例

// 參數求和
function sum (...values){
	return values.reduce((previous,current) => prvious + current, 0);
}

let sumProxy = new Proxy(sum,{
	apply:function(trapTarget,thisArg,argumentList){
		argumentList.forEach(arg => {
			if(typeof arg !== "number"){
				throw new TypeError("所有參數必須是數字,親!");
			}
		});
		return Reflect.apply(trapTarget,thisArg,argumentList);
	},
	// 防止使用new 關鍵字調用
	construct:function(trapTarget,argumentList){
		throw new TypeError("親,你不能這么干,該函數不能通過new調用。");
	}
});

// 測試哈
console.log(sumProxy(1,2,3,4)); // 10

// 傳入一個非數字的屬性值試試 【直接拋出異常】 
console.log(sumProxy(1,2,3,4)); // 10

// 同樣使用new調用 【直接拋出異常】 
let result = new sumProxy();

apply陷阱和Reflect.apply()都接受同樣的參數

  • trapTarget 被執行的函數(代理的目標)
  • thisArg 函數被調用時內部的this的值
  • argumentList傳遞給函數的參數數組

當使用new調用函數時 會觸發construct陷阱,接收的參數為

  • trapTarget 被執行的函數(代理的目標)
  • argumentList傳遞給函數的參數數組

其中Reflect.construct()第三個參數是newTarget 這是一個可選參數。用於指定該函數內部
new.target的值




看到這里有沒有感覺這個對於js項目代碼檢測還是蠻有用處的呢。



ok先到這里,時間不早了改且休息了;改天繼續…


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM