在实际开发中经常会遇到js抛出的错误,但是我们有没有想过自己去接管js异常验证,根据自己的需求抛出异常呢?原本也许不行,但是在es6出来后就可以做到了
什么是‘代理’ 呢?代理:就是调用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对象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陷阱来验证一下对象属性赋值操作(如为对象新增属性,要求必须赋值为int)
let target = {
name :'target'
};
let proxy = new Proxy(target,{
set(trapTarget,key,value,receiver){
//忽略不希望受到影响的已有属性
if(!trapTarget.hasOwnProperty(key)){
if(isNaN(value)){
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陷阱默认接收 四个参数
如
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)
参数
结合上面3、4 的操作可以封装一个带自定义检测的对象,为以后自己写插件打下基础
let originObj = {
employee:'jack'
};
let optObj = new Proxy(originObj,{
set(trapTarget,key,value,receiver){
// 忽略原有属性
if(!trapTarget.hasOwnProperty(key)){
if(!isNaN(value)){ // 属性不能位数字
throw new TypeError('属性不能为数字!')
}
}
// 添加属性
return Reflect.set(trapTarget,key,value,receiver);
},
get(trapTarget,key,receiver){
if(!(key in receiver)){
throw new TypeError('属性'+key+'不存在');
}
return Reflect.get(trapTarget,key,receiver);
}
});
用node测试哈
> optObj.sex = 'female';
'female'
> console.log(optObj.sex);
female
undefined
> console.log(originObj.sex);
female
undefined
> optObj.age =18;
Thrown:
TypeError: 属性不能为数字!
at Object.set (repl:6:14)
> console.log(optObj.age);
Thrown:
TypeError: 属性age不存在
at Object.get (repl:14:13)
> console.log(optObj.name)
Thrown:
TypeError: 属性name不存在
at Object.get (repl:14:13)
> console.log(optObj.employee)
jack
使用这个两个陷阱来验证函数调用时的参数的正确性
如下案例
// 参数求和
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()都接受同样的参数
当使用new调用函数时 会触发construct陷阱,接收的参数为
其中Reflect.construct()第三个参数是newTarget 这是一个可选参数。用于指定该函数内部
new.target的值
看到这里有没有感觉这个对于js项目代码检测还是蛮有用处的呢。
ok先到这里,时间不早了改且休息了;改天继续…