你可能忽略的代理小知识点

164 阅读3分钟

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

前言

hello,大家好,今天还是复习ES6的一天,今天复习的时候,总结一波大家可能没有覆盖的代理小知识点,今天我们就来谈一谈吧。

代理另一个代理

代理可以拦截反射 API 的操作,而这意味着完全可以创建一个代理,通过它去代理另一个代理。这样就可以在一个目标对象之上构建多层拦截网:  

const target ={
 foo :' bar ' ( 
); 

 constfirstProxy = new Proxy ( target ,{
 get (){ 
 console . log (' first proxy '); 
 return Reflect . get (... arguments );
)); 
 const secondProxy = new Proxy ( firstProxy ,{ 
 get (){
 console . log (' second proxy ');
 return Reflect . get (... arguments );
});
 console . log ( secondProxy . foo );
// second proxy 
/ first proxy 


代理的问题与不足

代理是在 ECMAScript 现有基础之上构建起来的一套新 API ,因此其实现已经尽力做到最好了。很大程度上,代理作为对象的虚拟层可以正常使用。但在某些情况下,代理也不能与现在的 ECMAScript 机制很好地协同。\

1.代理中的 this

代理潜在的一个问题来源是 this 值。我们知道,方法中的 this 通常指向调用这个方法的对象 

const target ={
 thisValEqualsProxy (){ return this === proxy ;

 const proxy = new Proxy ( target ,{0);
 console . log ( target . thisValEqualsProxy ());// false 
 console .1og( proxy . thisValEqualsProxy ());// true 
 }

这样完全没有问题:调用代理上的任何方法,比如 proxy . outerMethod (),而这个方法进而又会调用另一个方法,如 this . innerMethod (),实际上都会调用 proxy . innerMethod ()。多数情况下,这是符合预期的行为。可是,如果目标对象依赖于对象标识,那就可能碰到意料之外的问题。 还记得第6章中通过 WeakMap 保存私有变量的例子吧,以下是它的简化版:

class User {\
 constructor ( userId ){
 Wm . set ( thiS , userId ); 
 set id ( userId ){ 
 wm . set ( this , userId );
 getid (){ 
 return wm . get ( this );
 }

由于这个实现依赖 User 实例的对象标识,在这个实例被代理的情况下就会出问题: 

const user = new User (123);
 console . log ( user . id );//123
 const userInstanceProxy = new Proxy ( user ,{});
 console . log ( userInstanceProxy . id );// undefined 

这是因为 User 实例一开始使用目标对象作为 WeakMap 的键,代理对象却尝试从自身取得这个实例。要解决这个问题,就需要重新配置代理,把代理 User 实例改为代理 User 类本身。之后再创建代理的实例就会以代理实例作为 WeakMap 的键了:  

const UserClassProxy = new Proxy ( User ,(0);
const proxyUser = new UserClassProxy (456); console . log ( proxyUser . id );

2.代理与内部槽位

代理与内置引用类型(比如 Array )的实例通常可以很好地协同,但有些 ECMAScript 内置类型可 能会依赖代理无法控制的机制,结果导致在代理上调用某些方法会出错。 一个典型的例子就是 Date 类型。根据 ECMAScript 规范, Date 类型方法的执行依赖 this 值上的内部槽位[[ NumberDate ]]。代理对象上不存在这个内部槽位,而且这个内部槽位的值也不通过普通的 get (和 set (操作访问到,于是代理拦截后本应转发给目标对象的方法会抛出 TypeError :  

consttarget = neWDate ();
 Const prOxy = neWPrOxy ( target ,{});
 console . log ( proxy instanceof Date );// true 
 proxy - getDate ();// TypeError :' this ' is not a Date object