代理模式:为一个对象提供代用品或者占位符,以便控制对它的访问;应与实体应保持一致性,暴露同样的接口
代理模式可分虚拟代理、缓存代理、保护代理等,这里主要介绍下虚拟代理和缓存代理。
虚拟代理:将一个开销很大的任务延迟到真正需要它的时候再去执行
虚拟代理一个很好的例子是图片预加载,如果直接给img标签设置src属性,由于图片过大或者网络不佳,图片的位置往往会出现一段时间空白。可以先使用一张loading图占位,当图片加载好了之后再填充到img里。
// 图片加载
function loadImg() {
const img = new Image();
document.body.append(img);
return {
setSrc(src) {
img.src = src;
}
}
}
const remoteSrc = 'https://s.cn.bing.net/th?id=OHR.ChacoCulture_ZH-CN2098865361_1920x1080.webp&qlt=50';
loadImg().setSrc(remoteSrc)
// 虚拟代理方式
function proxyLoadImg() {
const img = new Image();
img.onload = () => {
realImg.setSrc(img.src)
}
const realImg = loadImg();
return {
setSrc(src) {
// 先设置loading占位,等图片加载好后再设置
realImg.setSrc('./loading.gif');
img.src = src;
}
}
}
proxyLoadImg().setSrc(remoteSrc)
其实不使用代理也可以实现,这里引用一下单一职责原则(就一个函数来说,应该仅有一个引起它变化的原因。如果多个职责耦合在一起,这个对象会变得巨大,多个职责耦合在一起,引起变化的原因就会多个)对于给img设置src来说,预加载只是一个锦上添花的功能,如果把这个操作分离开更好。
缓存代理:将计算结果进行缓存,当下次传入同样的参数时直接从缓存中去取结果,而不用再重新计算
function multi(...args) {
console.log('run')
return args.reduce((a, b) => a * b);
}
console.log(multi(1,3,5,7))
console.log(multi(1,3,5,7))
// 通用的缓存代理
function cacheProxy(fn) {
const cache = {};
return function(...args) {
if (!cache[args]) {
cache[args] = fn.apply(this, args);
}
return cache[args]
}
}
const cacheMulti = cacheProxy(multi);
console.log(cacheMulti(1,3,5,7))
console.log(cacheMulti(1,3,5,7)) // 只计算了一次