设计模式:有助于提高代码的复用性和可维护性
代理模式为一个对象提供一个代用品或占位符,以便控制它的访问。
当我们不方便直接访问某个对象时,或不满足需求时,可考虑使用一个替身对象来控制该对象的访问。替身对象可对请求预先进行处理,再决定是否转交给本体对象
用生活中的例子简单的描述一下
1、你自己不太方便买一件商品,可以通过找代购帮你买,然后代购买好东西后交给你。
2、租房或者买房,我们通常会找中介,中介按照我们的需求,帮我们选好几套房子,然后我们在进行对比,最后租到或者买到自己心仪的房子
像这种代购,中介,都可以称之为代理
特点
- 代理对象可预先处理请求,再决定是否转交给本体;
- 代理和本体对外显示接口保持一致性
- 代理对象仅对本体做一次包装
模式细分
- 虚拟代理(将开销大的运算延迟到需要时执行)
- 缓存代理(为开销大的运算结果提供缓存)
- 防火墙代理(控制网络资源的访问)
- 远程代理(为一个对象在不同的地址控件提供局部代表)
适用场景
虚拟代理:
- 图片预加载(loading 图)
- 合并HTTP请求(数据上报汇总)
缓存代理:(前提本体是纯函数)
- 缓存异步请求数据
- 缓存较复杂的运算结果
ES6 的 Proxy
- 实现对象私有属性
- 实现表单验证
优点
1、可拦截和监听外部对本体对象的访问;
2、复杂运算前可以进行校验或资源管理;
3、对象职能粒度细分,函数功能复杂度降低,符合 “单一职责原则”;
4、依托代理,可额外添加扩展功能,而不修改本体对象,符合 “开发-封闭原则”
缺点
1、额外代理对象的创建,增加部分内存开销;
2、处理请求速度可能有差别,非直接访问存在开销,但 “虚拟代理” 及 “缓存代理” 均能提升性能
代码实现
虚拟代理(延迟执行)
虚拟代理的目的,是将开销大的运算延迟到需要时再执行
模拟场景:图片懒加载
myImage = (function(){
var imgDom = document.createElement('img')
document.body.appendChild(imgDom)
return {
setSrc:function(src){
imgDom.src = src
}
}
})()
myImage.setSrc('图片路径')
//引入代理
myImage = (function(){
var imgDom = document.createElement('img')
document.body.appendChild(imgDom)
return {
setSrc:function(src){
imgDom.src = src //图片加载完设置真实图片src
}
}
})()
proxyMyImage = (function(){
var img = new Image()
img.onload = function(){
myImage.setSrc(this.src);
}
return {
setSrc:function(src){
myImage.setSrc('./loading.gif') //预先设置图片src为loading图
img.src = src
}
}
})()
模拟场景:http请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<input type="checkbox" id="1"></input>1
<input type="checkbox" id="2"></input>2
<input type="checkbox" id="3"></input>3
<input type="checkbox" id="4"></input>4
<input type="checkbox" id="5"></input>5
<input type="checkbox" id="6"></input>6
<input type="checkbox" id="7"></input>7
<script>
var synchronousFiles = function(id){
console.log('开始同步文件,id为:'+id)
}
var proxySynchronousFiles = (function(){
var cache = [] //保存一段时间内要缓存的id
var timer;
return function(id){
cache.push(id)
if(timer){
return;
}
timer = setTimeout(()=>{
var ids = cache.join(',')
synchronousFiles(ids) //2秒后向本体发送需要同步的id
clearTimeout(timer) //清除定时器
timer = null
cache = [] //清空数组
},2000)
}
})()
var checkbox = document.getElementsByTagName('input')
for(var i=0;i<checkbox.length;i++){
checkbox[i].onclick = function(){
if(this.checked == true){
proxySynchronousFiles(this.id)
}
}
}
</script>
</body>
</html>
代理缓存
缓存代理的目的,是为一些开销大的运算结果提供暂时存储,以便下次调用时,参数与结果不变情况下,从缓存返回结果,而不是重新进行本体运算,减少本体调用次数。
模拟场景:求阶乘和
var cache = {}
function multi(n){
if(n<1){
return;
}
if(n==1){
return 1;
}
return n * (cache[n-1] || multi(n - 1));
}
var sum = (function(){
var result = 0
return function(n){
for(var i=1;i<=n;i++){
let ret = multi(i)
cache[i] = ret
result += ret
}
return result;
}
})()
console.log(sum(3))