Proxy 代理器 - 图1
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”

构造函数

ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

这个构造函数接收两个参 数:目标对象和处理程序对象。缺少其中任何一个参数都会抛出 TypeError 。

  1. var proxy = new Proxy(target, handler);

创建空代理

最简单的代理是空代理,即除了作为一个抽象的目标对象,什么也不做。默认情况下,在代理对象上执行的所有操作都会无障碍地传 播到目标对象。因此,在任何可以使用目标对象的地方,都可以通过同样的方式来使用与之关联的代理对象。

要创建空代理,可以传一个简单的对象字面量作为处理程序对象,从而让所有操作畅通无阻地抵达目标对象。

如下面的代码所示,在代理对象上执行的任何操作实际上都会应 用到目标对象。唯一可感知的不同就是代码中操作的是代理对象。

image.png

image.png
image.png

定义捕获器

使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在 处理程序对象中定义的“基本操作的拦截器”。每个处理程序对象可以 包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接 或间接在代理对象上调用。每次在代理对象上调用这些基本操作时, 代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦 截并修改相应的行为。

静态方法 Proxy.xxx

revocable(target, handler) 创建可撤销的代理对象

Proxy.revocable()方法可以用来创建一个可撤销的代理对象

该方法的返回值是一个对象,其结构为:{"proxy": proxy, "revoke": revoke}

  • proxy 表示新生成的代理对象本身,和用一般方式 new Proxy(target, handler) 创建的代理对象没什么不同,只是它可以被撤销掉。
  • revoke 撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。

该方法常用于完全封闭对目标对象的访问, 如下示例

  1. const target = { name: 'vuejs'}
  2. const {proxy, revoke} = Proxy.revocable(target, handler)
  3. proxy.name // 正常取值输出 vuejs
  4. revoke() // 取值完成对proxy进行封闭,撤消代理
  5. proxy.name // TypeError: Revoked

实例方法

方法 描述
handler.has() in 操作符的捕捉器。
handler.get() 属性读取操作的捕捉器。
handler.set() 属性设置操作的捕捉器。
handler.deleteProperty() delete 操作符的捕捉器。
handler.ownKeys() Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
handler.apply() 函数调用操作的捕捉器。
handler.construct() new 操作符的捕捉器

get() 拦截属性读取

set() 拦截属性赋值

apply() 拦截方法调用、callapply

has() 拦截 HasProperty 操作

construct() 拦截 new 命令

deleteProperty() 拦截 delete 操作

defineProperty() 拦截 Object.defineProperty 操作

getOwnPropertyDescriptor()

Proxy 和 Object.defineProperty 区别

Object.defineProperty 只能监听到属性的读写,而 Proxy 除读写外还可以监听属性的删除,方法的调用等。

通常情况下我们想要监视数组的变化,基本要依靠重写数组方法的方式实现,这也是 Vue 的实现方式,而
Proxy 可以直接监视数组的变化。

  1. const list = [1, 2, 3];
  2. const listproxy = new Proxy(list, {
  3. set(target, property, value) {
  4. target[property] = value;
  5. return true; // 标识设置成功
  6. }
  7. });
  8. list.push(4);

Proxy 是以非入侵的方式监管了对象的读写,而 defineProperty 需要按特定的方式定义对象的属性。