代理模式
什么是代理模式
意图
当客户不方便直接访问一个对象或不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求作出一些处理后,再把请求转交给本体对象。
常见的代理模式
- 防火墙代理:控制网络资源的访问,保护主题不让”坏人“接近
- 远程代理:为一个对象在不同的地址空间提供局部代表。
- 保护代理:用于对象应该有不同反问权限的情况
- 智能引用代理:取代了简单的指针,它在访问对象时执行一些附加操作,比如计算一个对象被引用的次数
- 虚拟代理:虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建
- 缓存代理:缓存一些开销较大的结果,让其下次访问时,可以直接从缓存中获取
而在 JavaScript 中使用最常见的就是虚拟代理和缓存代理
例子
虚拟代理
在图片加载过程中,如果图片过大、过于多又或者网速慢的情况下,经常会造成网页上一些页面出现空白得情况。而最常见的做法是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img结点里。这种场景就很适合使用虚拟代理。
首先创建一个加载图片的本体对象
let myImage = (function(){
let imgNode = document.createElment('img')
document.body.appendChild('imgNode')
return {
setSrc: function(src) {
imgNode.src = src
}
}
})()
这时 myImage 还没使用代理模式,当调用 myImage.setSrc() 时,就只是直接的给 src 赋值,并没有任何的优化。此时我们加入代理模式,给 myImage 添加新的功能。
let proxyImage = (function(){
let img = new Image;
img.onload = function() {
myImage.setSrc(this.src)
}
return {
setSrc: function(src) {
myImage.setSrc('loading.jpg')
img.src = src
}
}
})()
proxyImage.setSrc('图片链接')
这里我们通过 proxyImage 间接地访问 myImage。proxyImage 控制了客户对 myImage 的访问,并且在此过程中加入一些额外的操作。
而且我们可以注意到代理的接口和本体的接口是保持一致性的,这样做的好处在于
- 用户可以放心地请求代理,他只关心是否能得到想要的结果
- 在任何使用本体的地方都可以替换成使用代理
缓存代理
缓存代理就如同他的名字一样,通过对代理对象处理的结果进行缓存。等待下一次调用时就可以直接返回缓存结果,从而节省了大量计算时间。从算法的角度上来讲就是空间换时间。
首先我们先写一个简单的求乘积的函数
let mult = function() {
let a = 1
for(let i=0, l = arguments.length;i<l;i++>) {
a = a * arguments[i]
}
return a
}
mult(2, 3) // 6
然后加入缓存代理模式
let proxyMult = (function() {
let cache = {}
return function() {
let args = Array.prototype.join.call(arguments, ',')
if(args in cache) { // 判断缓存表中是否已缓存过
return cache[args]
}
return cache[args] = mult.apply(this, arguments)
}
})()
proxyMult(1, 2, 3, 4) // 24
当第二次调用 proxyMult 计算相同的乘积时,就会从缓存当中提取结果返回,而不是从新计算。
当然我们可以进一步封装一下,让缓存代理模式可以随意传入一个计算函数进行代理。
let createProxyFactory = function(fn) {
let cache = {}
return function() {
let args = Array.prototype.join.call(arguments, ',')
if(args in cache) { // 判断缓存表中是否已缓存过
return cache[args]
}
return cache[args] = fn.apply(this, arguments)
}
}
let proxyMult = createProxyFactory(mult)
TIP
装饰模式和代理模式的区别之处在于,代理模式一定是自身持有这个对象,不需要从外部传入;而装饰模式的一定是需要从外部传入的,并且可以没有顺序,可以按照代码的实际需求随意调换顺序。
TIP
代理模式和适配器模式的最大的区别,就是代理模式是与原对象实现同一个接口;而适配器类则是匹配新接口,说白了,就是实现一个新的接口。