内容大部分来自 《javascript 设计模式与开发实践》一书,适当的结合自己工作实践,做出整理和笔记
系列: javascript 设计模式
代理模式
- 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
- 代理和本体的一致性要求,代理和本体需要对外提供一致的方法,代理请求接手的过程对用户来说是透明的,用户可以安心的访问代理,在任何地方都可以使用本体来代替代理。
- 代理模式体现在对本体对象的额外附带或者加强的能力上,让本体对象只处理原本的任务,符合单一职责原则。
常见代理模式
- 虚拟代理:把开销大的操作,放到真真需要的时候才会去执行
- 保护代理:控制不同权限的对象对目标对象的访问,Javascript并不容易实现保护代理,本章内容也主要讨论的是虚拟代理。例如:
判断需要给服务器发送消息,可以判断通过保护代理知道对方在线的时候再发消息给对方,这样在请求代理的时候可以直接返回对方不在线而拒绝掉。
虚拟代理 - 图片懒加载
在日常开发中需要对一个图片设置src,由于图片加载需要等待时间所以会出现图片空白的场景,下面来用代理模式来实现:
1 | // 额外添加一个方法获取一个一像素的色点 |
如上:
myImage 方法只处理和显示图片相关的逻辑,proxyImage 为图片显示加上了懒加载的能力,这样就符合 单一职责原则 某一天你不需要这里使用懒加载的功能也可以很方便的修改。
虚拟代理 - 惰性应用
惰性应用可以通过代理方法实现,在真真需要的时候才去执行对本地的调用。在一些需要较大的操作时,可以延迟或者按需对本体的执行,对于性能提升很有帮助。
不做详细演示
缓存代理
缓存带了可以为一些开销大的操作提升暂时存储的能力,如果传递进来的参数保持一致可以支持返回。
例如:我需要设计一个方法返回斐波那契函数前N个的数的数字是多少。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 // 先创建一个方法用于获取前N个的值分别是多少,0开始
function fibonacciArray(n) {
const res = [];
let i = 0;
while (i <= n) {
if (i <= 1) {
res.push(1);
} else {
res.push(res[i - 1] + res[i - 2])
}
i += 1;
}
return res;
}
console.time('fibonacciArray');
console.log(fibonacciArray(40));
console.timeEnd('fibonacciArray');
下面换一种思维通过递归调用获取需要第N个的值,如下方法会存在较大的性能问题1
2
3
4
5
6
7
8
9
10
11
12// 例子来源于前同事的
function fibonacci(n) {
if (n <= 1) {
return 1;
} else {
return fibonacci(n -2) + fibonacci(n -1);
}
}
console.time('fibonacci');
console.log(fibonacci(40));
console.timeEnd('fibonacci');
1 | // 这是一个失败了的例子并没用使用代理的痕迹,不修改源码的情况下递归调用会导致运行到本体函数 |
1 | // 本体操作,假设很耗时的操作 |
这样缓存的能力是由代理提供的,符合单一职责
原则
高阶函数动态创建缓存代理
创建代理函数工厂,缓存函数的执行结果,类似场景代理方法都可以通过该代理工厂创建
1 | // 本体操作,假设很耗时的操作 |
小结
代理模式包括许多小分类,在开发中最常用的是虚拟代理和缓存代理。
虽然代理模式非常有用,但我们在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式。
当真正发现不方便直接访问某个对象的时候,再编写代理也不迟。