理解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的资料请关注毛票票其它相关文章!