Skip to content

AHABHGK

代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问

保护代理与虚拟代理

  • 保护代理:代理帮主体过滤掉一些请求

  • 虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建

虚拟代理实现图片预加载

图片加载完成前的 loading 展位图

1const myImg = (function () {
2 const imgNode = document.createElement('img')
3 document.body.appendChild(imgNode)
4
5 return {
6 setSrc(src) {
7 imgNode.src = src
8 },
9 }
10})()
11
12const proxyImg = (function () {
13 const img = new Image()
14 img.onload = function () {
15 myImg.setSrc(this.src)
16 }
17
18 return {
19 setSrc(src) {
20 myImg.setSrc('./img/loading.gif')
21 img.src = src
22 }
23 }
24})()
25
26proxyImg.setSrc('http://abigimage.com/abigimage.png')

可进一步改进,封装成组件,暴露 setLoadingSrc 和 setSrc

代理和主体接口的一致性

如果有一天我们不再需要预加载图片,保证接口的一致性可以让我们在任何使用代理的地方切换成本体

在 JavaScript 中,如果代理对象和本体对象都是一个函数,函数必然都能被执行,则可认为他们有一致的“接口”

1const myImg = (function () {
2 const imgNode = document.createElement('img')
3 document.body.appendChild(imgNode)
4
5 return function (src) {
6 imgNode.src = src
7 }
8})()
9
10const proxyImg = (function () {
11 const img = new Image()
12 img.onload = function () {
13 myImg(this.src)
14 }
15
16 return function (src) {
17 myImg('./img/loading.gif')
18 img.src = src
19 }
20})()

虚拟代理合并 http 请求

一个文件列表,点击 checkbox 上传相应文件,如果选中过快,频繁的网络请求是一笔极大的开销

可以将多次网络请求(几秒钟内的)合并成一次,减小服务器的压力

1const uploadFiles = function (ids) {
2 // 对 ids 数组相应的文件进行处理
3 // 发送一次请求
4}
5
6const proxyUploadFiles = (function () {
7 const ids = []
8 let timer
9
10 return function (id) {
11 ids.push(id)
12
13 if (timer) {
14 return
15 }
16
17 timer = setTimeout(() => {
18 uploadFiles(ids)
19
20 clearTimeout(timer)
21 timer = null
22 ids.length = 0
23 }, 2000)
24 }
25})()
26
27// 事件委托

缓存代理

缓存计算结果:

1const calculate = function (...args) {
2 let result
3 // 一些消耗巨大的运算
4 return result
5}
6
7const proxyCalculate = (function () {
8 const cache = {}
9
10 return function (...args) {
11 const key = args.join(',')
12
13 if (cache[key]) {
14 return cache[key]
15 }
16
17 cache[key] = calculate(...args)
18 return cache[key]
19 }
20})()

进一步解耦,修改为创建代理的工厂,更有通用性

1const createProxy = function (fn) {
2 const cache = {}
3
4 return function (...args) {
5 const key = args.join(',')
6
7 if (cache[key]) {
8 return cache[key]
9 }
10
11 cache[key] = fn(...args)
12 return cache[key]
13 }
14}
15
16const mult = function (...args) {
17 args.reduce((current, next) => current * next)
18}
19
20const plus = function (...args) {
21 args.reduce((current, next) => current + next)
22}
23
24const proxyMult = createProxy(mult)
25const proxyPlus = createProxy(plus)
26
27console.log(proxyMult(1, 2, 3))
28console.log(proxyPlus(1, 2, 3))

缓存请求:

ES6 中的代理模式

Proxy、Reflect

new Proxy() 这个实例就是对象的代理

其他代理模式

  • 防火墙代理:控制网络资源的访问,保护主题不让“坏人”接近。

  • 远程代理:为一个对象在不同的地址空间提供局部代表,在 Java 中,远程代理可以是另一个虚拟机中的对象。

  • 保护代理:用于对象应该有不同访问权限的情况。

  • 智能引用代理:取代了简单的指针,它在访问对象时执行一些附加操作,比如计算一个对象被引用的次数。

  • 写时复制代理:通常用于复制一个庞大对象的情况。写时复制代理延迟了复制的过程,当对象被真正修改时,才对它进行复制操作。写时复制代理是虚拟代理的一种变体,DLL(操作系统中的动态链接库)是其典型运用场景。