9 代理与反射

2 阅读3分钟

提供了拦截并向基本操作嵌入额外行为的能力,比如vue3中实现的动态数据绑定ref,就是在写入动态数据的时候,将写入操作拦截,然后在写入原对象的同时更新到页面上

代理基础

代理就是目标对象的抽象,类似于指针,代理对象执行的任何操作实际上都会应用到目标对象,目标对象中属性的变化也能在代理对象中感知到

构建

const proxy = new Proxy(目标对象,处理程序对象)

  • 其中处理程序对象就是一组基本操作拦截器(捕获器)函数组成的对象
const target = {a:1};
const handler = {
    set(){
      console.log('这是拦截的属性修改操作');
    },
    get(){
      console.log('这是拦截的属性获取操作')
    }
};

const proxy = new Proxy(target,handler);
proxy.a; //这是拦截的属性获取操作
proxy.a = 2; //这是拦截的属性修改操作

console.log(target);//{ a: 1 },因为拦截后并没有写入
console.log(proxy);//{ a: 1 }

捕获器

捕获器就是对象属性基本操作对应的函数,比如修改属性=对应set(),每种操作都对应不同的捕获器函数

捕获器参数

不同捕获器有不同的参数,基本操作会自动传入这些操作

const target = {a:1};
const handler = {
    set(){
      console.log(...arguments);//目标对象 属性 值 代理对象
    },
    get(){
      console.log(...arguments);//目标对象 属性 代理对象
    }
};

const proxy = new Proxy(target,handler);
proxy.a; //{ a: 1 } a { a: 1 }
proxy.a = 2; //{ a: 1 } a 2 { a: 1 }

捕获器不变式

虽然可以捕获器能重构对应的基本操作,但是得操作合法性,比如不能对[[Writable]]为false的属性进行赋值;因此不同的捕获器有不同的捕获器不变式

反射

正是避免捕获器不变式导致重构操作需要对基本操作进行重复造轮子,将基本操作对应的函数全部封装在Reflex对象中,在增加自己的操作后,就直接调用反射API传入参数即可

const target = {a:1};
Object.defineProperty(target,'b',{
  enumerable:true,
  value:2   //其他不写则默认为default
});
const handler = {
    set(){
      console.log(Reflect.set(...arguments))
    },
    get(){
      console.log(...arguments);//目标对象 属性 代理对象
    }
};

const proxy = new Proxy(target,handler);
proxy.b = 3; //false,部分反射API操作不成功时返回false,成功时为true

console.log(target);//{ a: 1, b: 2 },b值没变,因为writable为false
  • 反射API并不局限于捕获器
  • 大多数反射API在Object上有对应的方法
  • 很多反射API操作后返回true/false,而对应的Object方法直接抛出错误
  • 部分API可以替代操作符
  • 部分对象可能自定义了apply(),为此可以使用Reflect.apply()来避免这种情况发生

可撤销代理

可中断代理对象于目标对象之间的联系

构建

const {proxy,revoke} = Proxy.revocable(目标对象,处理程序对象)

const target = {b:3};

const handler = {
    set(){
      console.log(...arguments);//目标对象 属性 值 代理对象
      Reflect.set(...arguments)
    },
    get(){
      return 'interface'
    }
};

const {proxy,revoke} = Proxy.revocable(target,handler);
console.log(proxy.b); //interface
console.log(target.b);//3
revoke();// 中断代理的联系
console.log(proxy.b);//TypeError

代理中的问题

  • this指针,虽然通过代理能调用目标对象的函数,但是函数中的this指向的是代理对象

代理捕获器与反射方法

13种捕获器函数于反射API的返回限制、对应的拦截操作、传入参数和捕获器不变式

代理模式

使用代理能实现的功能

  • 隐藏属性
    在get、has捕获器中判断是否为要隐藏的属性,是则返回undefined
  • 属性验证 在set中,根据所赋的值决定是接受还是拒绝
  • 动态数据