Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”
构造函数
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
这个构造函数接收两个参 数:目标对象和处理程序对象。缺少其中任何一个参数都会抛出 TypeError 。
var proxy = new Proxy(target, handler);
创建空代理
最简单的代理是空代理,即除了作为一个抽象的目标对象,什么也不做。默认情况下,在代理对象上执行的所有操作都会无障碍地传 播到目标对象。因此,在任何可以使用目标对象的地方,都可以通过同样的方式来使用与之关联的代理对象。
要创建空代理,可以传一个简单的对象字面量作为处理程序对象,从而让所有操作畅通无阻地抵达目标对象。
如下面的代码所示,在代理对象上执行的任何操作实际上都会应 用到目标对象。唯一可感知的不同就是代码中操作的是代理对象。
定义捕获器
使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在 处理程序对象中定义的“基本操作的拦截器”。每个处理程序对象可以 包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接 或间接在代理对象上调用。每次在代理对象上调用这些基本操作时, 代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦 截并修改相应的行为。
静态方法 Proxy.xxx
revocable(target, handler) 创建可撤销的代理对象
Proxy.revocable()方法可以用来创建一个可撤销的代理对象
该方法的返回值是一个对象,其结构为:{"proxy": proxy, "revoke": revoke}
- proxy 表示新生成的代理对象本身,和用一般方式
new Proxy(target, handler)
创建的代理对象没什么不同,只是它可以被撤销掉。 - revoke 撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。
该方法常用于完全封闭对目标对象的访问, 如下示例
const target = { name: 'vuejs'}
const {proxy, revoke} = Proxy.revocable(target, handler)
proxy.name // 正常取值输出 vuejs
revoke() // 取值完成对proxy进行封闭,撤消代理
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() 拦截方法调用、call
、apply
has() 拦截 HasProperty
操作
construct() 拦截 new
命令
deleteProperty() 拦截 delete
操作
defineProperty() 拦截 Object.defineProperty
操作
getOwnPropertyDescriptor()
Proxy 和 Object.defineProperty 区别
Object.defineProperty
只能监听到属性的读写,而 Proxy
除读写外还可以监听属性的删除,方法的调用等。
通常情况下我们想要监视数组的变化,基本要依靠重写数组方法的方式实现,这也是 Vue 的实现方式,而Proxy
可以直接监视数组的变化。
const list = [1, 2, 3];
const listproxy = new Proxy(list, {
set(target, property, value) {
target[property] = value;
return true; // 标识设置成功
}
});
list.push(4);
Proxy
是以非入侵的方式监管了对象的读写,而 defineProperty
需要按特定的方式定义对象的属性。