前言Proxy可以理解为在目标对象前面设置了一层“拦截”,外界对该对象的访问必须先经过这一层拦截,所以提供了一种机制让对外界的访问过滤和重写。Proxy这个词的本意是代理,这里用它来表示它会“代理”某些操作,可以翻译为“代理”。语法varproxy=newProxy(target,handler);Proxy对象的所有用法都是上面的形式,唯一不同的是handler参数的写法。其中:newProxy()表示生成一个Proxy实例。target参数表示要拦截的目标对象。handler参数也是一个对象,用于自定义拦截行为。如果handler没有设置任何拦截,就相当于直接指向了原来的对象。lettarget={};lethandler={};letproxy=newProxy(target,handler);proxy.name='ProgrammingSamadhi';控制台日志(目标名称);//还有一个'ProgrammingSamadhi'的技巧是将Proxy对象设置为object.proxy属性来调用object对象:letproxy=newProxy({},{get:function(target,property){return35;}});letobj=Object.create(proxy);obj.time//35get()get()方法用于拦截某个属性的读取操作,可以接受三个参数,顺序为:目标对象属性名代理实例本身(严格来说,是动作所针对的对象),可选。上面已经有get()方法的用法示例,下面是另一个拦截读取操作的示例:letperosn={name:'james',age:26,profession:'software'}varproxy=newProxy(perosn,{get:function(target,property){if(propertyintarget){returntarget[property];}else{thrownewReferenceError("propertype\""+property+"\"doesnoexit");}}});console.log(proxy.name,proxy.profession);//jamessoftwareconsole.log(proxy.sex);//UncaughtReferenceError:propertytype"sex"doesnoexitset()set()方法用于拦截某个属性的赋值操作,它可以接受四个参数,分别是:目标对象属性名属性值Proxy实例本身,选修的。假设person对象有一个age属性,应该是一个不大于200的整数,那么可以使用Proxy来保证age属性值满足要求。让perosn={name:'james',age:26,profession:'software'}letproxy=newProxy(perosn,{get:function(target,property){if(propertyintarget){returntarget[property];}else{thrownewReferenceError("propertype\""+property+"\"doesnoexit");}},set:function(target,key,value){if(key==='age'){if(value>80){throwReferenceError("invail");}else{returntarget[key]=value;}}else{returntarget[key];}}});proxy.age=60;console.log(proxy.name,proxy.profession,proxy.age);//詹姆斯软件60proxy.age=99;//UncaughtReferenceError:invalidapply()apply()方法拦截:函数调用calloperationapplyoperationapply()方法可以接受三个参数,分别是:目标对象的上下文对象(this)和目标的参数数组目的。lettwice={apply(target,ctx,agrs){returnReflect.apply(...arguments)*2;}};functionsum(a,b){returna+b;}letproxy5=newProxy(sum,twice);console.log(proxy5(1,3));//8console.log(proxy5.apply(null,[1,3]));//8另外,直接调用Reflect.apply方法也会被拦截。Reflect.apply(proxy5,null,[9,10])//38has()has()方法用于拦截HasProperty操作,即判断对象是否具有某个属性时,该方法才会生效。一个典型的操作是in操作符。has()方法可以接受两个参数,分别是目标对象和要查询的属性名。以下示例使用has方法对in运算符隐藏某些属性。letstu1={name:'张三',score:59};letstu2={name:'李四',score:99};lethandler={has(target,prop){if(prop==='score'&&target[prop]<60){console.log(`${target.name}failed`);返回假;}返回目标中的道具;}}letoproxy1=newProxy(stu1,handler);letoproxy2=newProxy(stu2,handler);'score'inoproxy1//张三失败//false'score'inoproxy2//truefor(letainoproxy1){console.log(oproxy1[a]);}//张三//59for(letbinoproxy2){console.log(oproxy2[b]);}//李四//上面99的代码,has拦截只对in操作符有效,for...in循环不起作用,导致不符合要求的属性没有被for...in循环排除。Proxy支持的拦截操作一览Proxy支持的拦截操作基本有13种。get(target,propKey,receiver)拦截对象属性的读取,如:proxy.fooproxy['foo']。set(target,propKey,value,receiver)拦截对象属性的设置,比如proxy.foo=v或者proxy['foo']=v,返回一个布尔值。has(target,propKey)拦截propKey在proxy中的操作,返回一个boolean值。deleteProperty(target,propKey)拦截deleteproxy[propKey]的操作,返回一个boolean值。ownKeys(target)拦截:Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in上面的拦截循环都是返回一个数组。该方法返回目标对象的所有属性的属性名,而Object.keys()的返回结果只包含目标对象本身的可遍历属性。getOwnPropertyDescriptor(target,propKey)拦截Object.getOwnPropertyDescriptor(proxy,propKey),返回属性的描述对象。defineProperty(target,propKey,propDescs)拦截:Object.defineProperty(proxy,propKey,propDescs)Object.defineProperties(proxy,propDescs)返回一个布尔值。preventExtensions(target)拦截Object.preventExtensions(proxy),返回一个布尔值。getPrototypeOf(target)拦截Object.getPrototypeOf(proxy)并返回一个对象。isExtensible(target)拦截Object.isExtensible(proxy)并返回一个布尔值。setPrototypeOf(target,proto)拦截Object.setPrototypeOf(proxy,proto),返回一个布尔值。如果目标对象是一个函数,那么还有两个额外的动作可以被拦截。apply(target,object,args)将Proxy实例的操作拦截为函数调用,如:proxy(...args)proxy.call(object,...args)proxy.apply(...)。construct(target,args)将Proxy实例的操作拦截为构造函数调用,如:newproxy(...args)。~~本文到此结束,感谢阅读!~学习有趣的知识,认识有趣的朋友,塑造有趣的灵魂!大家好,我是〖编程三昧〗的作者王隐,我的公众号是《编程三昧》,欢迎关注,希望大家多多指教!
