feng xiaohan

TS高级-Proxy和Reflect

Proxy

ES6 新增对象代理,相当于一个拦截器

详情见 JS 代理(Proxy)一章。

基本使用

let person = { name: "张三", age: 18 };

person.name; // 取值操作
person.name = "李四"; // 赋值操作

let personProxy = new Proxy(person, {
  // 创建一个拦截器 @1
  // 拦截取值操作
  get() {},
  // 拦截赋值操作
  set(target, key, value, receiver) {
    // @2
    return true;
  },
  // 拦截函数调用
  apply() {},
  // 拦截in操作符,
  has() {},
  // 拦截for in
  ownKeys() {},
  // 拦截new操作符
  construct() {},
  // 拦截delete操作符
  deleteProperty() {},
});

@1:拦截器接收两个参数:

  • target:需要被代理的对象(只接收引用数据类型:对象、数组、函数、set、map);
  • handler:一个通常以函数作为属性的对象,处理器对象占位符,对处理对象的描述;

@2:set 接收四个参数:

  • target:代理对象本身(此处为 person);
  • key:操作的属性(此处为 name);
  • value:操作的属性的值(此处为李四);
  • receiver:也是代理对象本身,主要是为了内部嵌套多个函数时保证上下文的正确。

Reflect

反射。和 Proxy13 个代理方法是一样的。但是它可以直接操作对象。

get()

可以通过 Reflect.get()来获取一个对象的属性:

Reflect.get(target, key, receiver);
  • target:需要被反射的对象(只接收引用数据类型:对象、数组、函数、set、map);
  • key:需要获取值的属性;
  • receiver:也是被反射的对象本身,主要是为了内部嵌套多个函数时保证上下文的正确。

例如:

let person = { name: "张三", age: 18 };

console.log(person.name); // 可以直接获取
console.log(Reflect.get(person, "name")); // 通过Reflect.get()获取

set()

可以通过 Reflect.set()来修改一个对象的属性:

Reflect.get(target, key, value, receiver);
  • target:需要被反射的对象(只接收引用数据类型:对象、数组、函数、set、map);
  • key:需要修改值的属性;
  • value:修改的值;
  • receiver:也是被反射的对象本身,主要是为了内部嵌套多个函数时保证上下文的正确。
console.log(Reflect.set(person, "name", "李四", person)); // true
console.log(person); // { name: "李四", age: 18 }

返回值:true/false

示例

let person = { name: "张三", age: 18 };

let personProxy = new Proxy(person, {
  get(target, key, receiver) {
    if (target.age <= 18) {
      return Reflect.get(target, key, receiver);
    } else {
      return "已成年";
    }
  },
});

cnosole.log(personProxy.age); // 18

实现观察者模式

const list: Set<Function> = new Set();

const autoRun = (cb: Function) => {
  if (!list.has(cb)) {
    list.add(cb);
  }
};

const observable = <T extends object>(params: T) => {
  // Proxy代理对象只支持引用数据类型,所以需要限制传入的类型
  return new Proxy(params, {
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      list.forEach((fn) => fn());
      return result;
    },
  });
};

测试观察者模式:

// 设置一个可供观察的对象
const personProxy = observable({ name: "张三", attr: "摸鱼" });

autoRun(() => {
  // 订阅函数
  console.log("有变化了");
});

personProxy.name = "李四"; // 有变化了 @1

@1:在尝试修改的时候观察对象的值时,会调用订阅的函数。修改几次数据将会执行几次订阅函数