理解JavaScript中的Proxy 与 Reflection API
一、创建Proxy
lettarget={}
letproxy=newProxy(target,{})
proxy.name="proxy"
console.log(proxy.name)//proxy
console.log(target.name)//proxy
target.name="target"
console.log(proxy.name)//target
console.log(target.name)//target
在上面的例子中,由Proxy构造器创建的proxy对象会将自身的所有操作直接转发给target。
当proxy.name被赋值为"proxy"时,target对象也会创建name属性并获得同样的值。实际上proxy对象本身并不创建和存储name属性,它只是转发对应的操作给target。
类似的,proxy.name与target.name的值始终保持一致,因为它们实际上都指向了target.name。这也意味着给target.name赋予一个新的值时,该变化也会反映到proxy.name上。
使用setTrap验证属性
Proxy允许开发者主动拦截本该转发给target对象的底层操作,这些拦截行为通过trap实现。每个trap都可以覆盖JavaScript对象的某些内置行为,即proxy允许通过trap拦截并修改指向target对象的操作。
假设需要创建一个新添加的属性值只能是数字类型的对象,就可以借助settrap覆盖默认的赋值行为。代码如下:
lettarget={
name:"target"
}
letproxy=newProxy(target,{
set(trapTarget,key,value,receiver){
if(!trapTarget.hasOwnProperty(key)){
if(isNaN(value)){
thrownewTypeError("Newpropertymustbeanumber.")
}
}
returnReflect.set(trapTarget,key,value,receiver)
}
})
proxy.count=1
console.log(proxy.count)//1
console.log(target.count)//1
proxy.name="proxy"
console.log(proxy.name)//proxy
console.log(target.name)//proxy
proxy.anotherName="proxy"
//TypeError:Newpropertymustbeanumber.
settrap中的四个参数含义如下:
- trapTarget:接收新属性的对象(即proxy指向的target)
- key:新属性对应的key
- value:新属性对应的value
- receiver:通常为proxy自身
Reflect.set()是与settrap相对应的原始方法,表示被覆盖前的默认的赋值行为。
使用getTrap令程序读取不存在属性时报错
JavaScript在读取不存在的属性时并不会报错,而是返回undefined。
lettarget={}
console.log(target.name)//undefined
可以借助gettrap修改读取对象属性时的默认行为:
letproxy=newProxy({},{
get(trapTarget,key,receiver){
if(!(keyinreceiver)){
thrownewTypeError("Property"+key+"doesn'texist.")
}
returnReflect.get(trapTarget,key,receiver)
}
})
proxy.name="proxy"
console.log(proxy.name)//proxy
console.log(proxy.nme)
//TypeError:Propertynmedoesn'texist.
通过deletePropertyTrap防止删除属性
JavaScript中使用delete操作符删除对象的属性:
lettarget={
name:"target",
value:42
}
Object.defineProperty(target,"name",{configurable:false})
console.log("value"intarget)//true
letresult1=deletetarget.value
console.log(result1)//true
console.log("value"intarget)//false
letresult2=deletetarget.name
console.log(result2)//false
console.log("name"intarget)//true
使用deleteProxyTrap防止属性被意外删除:
lettarget={
name:"target",
value:42
}
letproxy=newProxy(target,{
deleteProperty(trapTarget,key){
if(key==="value"){
returnfalse
}else{
returnReflect.deleteProperty(trapTarget,key)
}
}
})
console.log("value"inproxy)//true
letresult1=deleteproxy.value
console.log(result1)//false
console.log("value"inproxy)//true
letresult2=deleteproxy.name
console.log(result2)//true
console.log("name"inproxy)//false
二、Proxy的现实应用
logging
functionmakeLoggable(target){
returnnewProxy(target,{
get:(target,property)=>{
console.log("Reading"+property)
returntarget[property]
},
set:(target,property,value)=>{
console.log("Writingvalue"+value+"to"+property)
target[property]=value
}
})
}
letninja={name:"Yoshi"}
ninja=makeLoggable(ninja)
console.log(ninja.name)
ninja.weapon="sword"
//Readingname
//Yoshi
//Writingvalueswordtoweapon
性能测试
functionisPrime(number){
if(number<2){returnfalse}
for(leti=2;i{
console.time("isPrime")
constresult=target.apply(thisArg,args)
console.timeEnd("isPrime")
returnresult
}
})
console.log(isPrime(1358765377))
//isPrime:6815.107ms
//true
自动添加属性
functionFolder(){
returnnewProxy({},{
get:(target,property)=>{
console.log("Reading"+property)
if(!(propertyintarget)){
target[property]=newFolder()
}
returntarget[property]
}
})
}
constrootFolder=newFolder()
rootFolder.ninjasDir.firstNinjaDir.ninjaFile="yoshi.txt"
//ReadingninjasDir
//ReadingfirstNinjaDir
console.log(rootFolder.ninjasDir.firstNinjaDir.ninjaFile)
//ReadingninjasDir
//ReadingfirstNinjaDir
//ReadingninjaFile
//yoshi.txt
参考资料
https://leanpub.com/understandinges6
https://www.manning.com/books/secrets-of-the-javascript-ninja-second-edition
以上就是理解JavaScript中的Proxy与ReflectionAPI的详细内容,更多关于JavaScript中的Proxy与ReflectionAPI的资料请关注毛票票其它相关文章!