在實際開發中經常會遇到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先到這里,時間不早了改且休息了;改天繼續…