红宝书笔记之第九章:代理与反射

101 阅读3分钟

代理基础

创建空代理

代理是使用Proxy构造函数创建的,接受两个参数:目标对象和处理程序对象;

看看下面代码感受一下:

// 目标对象
const target = {
  id: 'target'
}
// 处理函数
const handler = {}
// 创建一个代理
const proxy = new Proxy(target, handler)

// id的属性会访问同一个值
console.log(target.id === proxy.id)  //这两个值都是target

// 给目标对象或者代理对象赋值对反映在两个对象上
target.id = "foo"
console.log(target.id)  // foo
console.log(proxy.id)    // foo

proxy.id = 'bar'
console.log(target.id)  // bar
console.log(proxy.id)    // bar

// Proxy.prototype是undefined,因此不能使用instanceof操作符
console.log(Proxy.prototype) // undefined

// 代理对象和目标对象是不同的两个对象  
console.log(target == proxy)  // false
定义捕获器

使用代理的主要目的就是定义捕获器,其实就是操作之前的拦截器;

触发条件有很多种:

例如:proxy[prototype]、proxy.prototype、Object.create(proxy)[prototype]等操作都可以触发get()捕获器;(P268页)

捕获器参数和反射API

所有的捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为;

比如get()捕获器会接受到目标对象,要查询的属性和代理对象三个参数;

get(trapTarget,property,receiver)  //三个参数

所有的捕获器都可以基于自己的参数重建原始操作,但并非所有的捕获器行为都想get()那么简单;实际上,开发者并不需要手动重建原始行为,而可以通过调用全局Reflect对象的同名方法来轻松重建;

捕获器的不可式

比如:如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回 一个与该属性不同的值时,或抛出TypeError

可撤销代理

对于使用new Proxy()创建的普通代理来说,这个联系会在代理对象的生命周期一致持续存在;

Proxy也暴露了revocable()方法,这个方法支持撤销代理对象和目标对象的关系。

注意事项:

  • 1,这种操作是不可逆的
  • 2,撤销函数(revoke())是幂等的,调用多少次结果都一样;
  • 3,撤销代理之后在调用代理会抛出TypeError;
实用反射API

首先介绍一下Reflect 对象

Reflect 对象不是构造函数,所以创建时不是用 new 来进行创建。

==在 ES6 中增加这个对象的目的:==

  • 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。
  • 修改某些 Object 方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc)则会返回 false。
  • 让 Object 操作都变成函数行为。某些 Object 操作是命令式,比如 name in obj 和 delete obj[name],而 Reflect.has(obj, name)和 Reflect.deleteProperty(obj, name)让它们变成了函数行为。
  • Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。

代理捕获器与反射方法

常用的方法(13个):

  • get
  • set
  • has
  • defineProperty
  • getOwnPropertyDescriptor
  • deleteProperty
  • ownKeys
  • getPrototypeOf
  • setPrototypeOf
  • isExtendsible
  • preventExtendsions
  • apply
  • constructor

用法都很简单,具体细节可以看看书中(P274)

代理模式

使用代理可以在代码中实现一些有用的编程

  • 1,跟踪属性访问
  • 2,隐藏属性
  • 3,属性检验
  • 4,函数与构造函数参数验证
  • 5,数据绑定与可观察行