# Proxy（代理模式）

Proxy（代理模式）属于结构型模式，通过访问代理对象代替访问原始对象，以获得一些设计上的便捷。

**意图：为其他对象提供一种代理以控制这个对象的访问。**

## 举例子

如果看不懂上面的意图介绍，没有关系，设计模式需要在日常工作里用起来，结合例子可以加深你的理解，下面我准备了三个例子，让你体会什么场景下会用到这种设计模式。

### 获得文本对象长度

获得一个文本对象长度，必须要真正渲染出来，而渲染是比较耗时的，我们可能只在某些场景下需要访问文本对象长度，而更多时候只需要读取文本内容，这两种操作耗时是完全不同的，如何做到业务层调用无感知，来优化执行耗时呢？

代理模式可以解决这个问题，我们将业务层使用的文本对象替换为代理对象，这个代理对象初始化并不渲染文本，而是在调用文本长度时才渲染。

### 对象访问保护

某个大型系统开发完了，突然要求增加代码访问权限体系，不同模块对相同的底层对象拥有不同访问权限，此时这个权限控制逻辑如果写入底层对象，就违背了开闭原则，而对象本身的实现也不再纯粹，增加了维护成本，如何做到不修改对象本身，实现权限控制呢？

代理模式也能解决，将底层对象导出替换为代理对象，由代理对象控制访问权限即可。

### 对象与视图双向绑定

Angular 或 Vue 这类前端框架采用双向绑定视图更新技术，即对象修改后，使用到的视图会自动刷新，这就需要做到以下两点：

1. 在对象被访问时，记录调用的视图绑定。
2. 在对象被修改时，刷新调用它的视图。

问题是，在业务代码使用对象与修改对象的地方插入这段逻辑，显然会增加巨大的维护成本，如何做到业务层无感知呢？

代理模式可以很好的解决这个问题，其实业务层拿到的对象已经是代理对象了，它在被访问与被修改时，都会执行固定的钩子做视图绑定与视图刷新。

## 意图解释

**意图：为其他对象提供一种代理以控制这个对象的访问。**

代理模式的意图很容易理解，就是通过代理对象代替原始对象的访问。

这只是代理模式的实现方式，代理模式真正的难点不在于理解它是如何工作的，而是理解哪些场景适合用代理，或者说创建了代理对象，怎么用才能发挥它的价值。

在上面例子中，已经举出了几种常见代理使用场景：

1. 对开销大的对象使用代理，以按需使用。
2. 对需要保护的对象进行代理，在代理层做权限控制。
3. 在对象访问与修改时要执行一些其他逻辑，适合在代理层做。

## 结构图

<img width=600 src="https://img.alicdn.com/imgextra/i3/O1CN01eZHGHQ28t0oeHYzas_!!6000000007989-2-tps-1262-522.png">

使用时关系如下：

<img width=600 src="https://img.alicdn.com/imgextra/i4/O1CN01iwyMKQ1KbOnR0N2AP_!!6000000001182-2-tps-1270-206.png">

Subject 定义的是 RealSubject 与 Proxy 共用的接口，这样任何使用 RealSubject 的地方都可以使用 Proxy。

RealSubject 指的是原始对象，Proxy 是一个代理实体。

关系图中可以看出，当客户端要访问 subject 时，第一层访问的是 Proxy 代理，由这个代理将 realSubject 转发给客户端。

## 代码例子

下面例子使用 typescript 编写。

```typescript
// 对象 obj
const proxy = new Proxy(obj, {
  get(target,key) {}
  set(target,key,value) {}
})
```

JS 创建代理还是蛮简单的，代理可以控制对象的所有成员属性，包括成员变量与成员方法的访问（get）与修改（set）。

## 弊端

代理模式会增加微弱的开销，因此请不要将所有对象都变成代理，没有意义的代理只会徒增程序开销。

另外代理对象过多，也会导致调试困难，因为代理层的存在，我们往往可能忽略这一层带来的影响，导致忘记这个对象其实是一个代理。

## 总结

代理和继承有足够多的相似之处，继承中，子类几乎可以人为是对父类的代理，子类可以重写父类的方法。但代理和继承还是有区别的：

如果你没有采用 `new Proxy` 这种 API 创建代理，而是采用继承的方式实现，你会一下子继承这个类的所有方法，而做不到按需控制访问权限的灵活效果，所以代理比继承更加灵活。

JS 的 `new Proxy` 对应了 Java 动态代理模式，一般认为动态代理比静态代理更强大。

最后，还要重申那句话，代理模式理解与运用并不难，难就难在能否在恰当的场合想到它，双向绑定几乎是代理模式最好的例子。

> 讨论地址是：[精读《设计模式 - Proxy 代理模式》· Issue #291 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/291)

**如果你想参与讨论，请 [点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**

> 关注 **前端精读微信公众号**

<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

> 版权声明：自由转载-非商用-非衍生-保持署名（[创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)）
