模式代理
# 什么是代理模式
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
比如,明星都有经纪人作为代理。如果想请明星来办一场商业演出,只能联系他的经纪人。经纪人会把商业演 出的细节和报酬都谈好之后,再把合同交给明星签。
javascript 中 proxy,nginx , webpack, vite 等的代理。
# 实现一个代理模式
# 图片预加载
在加载图片的时候,如果直接给图片一个 src 属性,如果图片太大或者网络不好的情况下就会出现一块空白区域,我们可以先用一张 loading 图片占位,然后异步加载图片,等图片加载完毕在把他填充到 img 的 src 中去。
var myImage = (function () {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src;
},
};
})();
myImage.setSrc('//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png');
使用一层代理
var proxyImage = (function () {
var img = new Image();
img.onload = function () {
myImage.setSrc(this.src);
};
return {
setSrc: function (src) {
myImage.setSrc(
'https://tiebapic.baidu.com/forum/w%3D120%3Bh%3D120/sign=31dc183a627f9e2f7035190a2f0b8119/0df431adcbef7609db6133436bdda3cc7dd99e80.jpg?tbpicau=2022-09-19-05_08b14b6d7860ca07829e8c1078db3e4c'
);
img.src = src;
},
};
})();
我们通过 proxyImage 间接地访问 myImage 。 proxyImage 控制了客户对 myImage 的访问,并且在此过程中加入一些额外的操作,在真正的图片加载好之前,先把 img 节点的 src 设置为 一张本地的 loading 图片。
这里的代理就是对 myImage 添加了一层行为控制(未加载完图片之前,加载一张 loading 图)。如果有一天你不在需要这个代理,那么直接访问本体对象就可以。
这里用到了虚拟代理,虚拟代理就是把一些开销很大的对象,延迟到真正需要它的时候才去创建,这也是虚拟代理在惰性加载中的使用。
# 合并请求
比如说一个实时性要求比较高的系统来说,需要频繁的去服务器获取数据,这个时候就可以使用一层代理合并请求,有点节流的味道了,
const proxyHttp = (function () {
const cache = [];
// 保存一段时间内需要同步的 ID timer; // 定时器
return function (id) {
cache.push(id);
if (timer) {
// 保证不会覆盖已经启动的定时器 return; }
timer = setTimeout(function () {
http(cache.join(','));
clearTimeout(timer); // 清空定时器
timer = null;
cache.length = 0; // 清空 ID 集合
}, 2000);
}
};
})();
# 缓存代理
// 乘积
const mult = function () {
console.log('mult star');
let res = 1;
for (let i = 0, l = arguments.length; i < l; i++) {
res = res * arguments[i];
}
return res;
};
mult(2, 3);
// mult star
// 6
mult(2, 3, 4);
// mult star
// 24
// 加入缓存代理函数:
const proxyMult = (function () {
var cache = {};
return function () {
var 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);
proxyMult(1, 2, 3, 4);
// mult star
// 24
// 24
第二次调用的时候并没有重新结算,而是使用了之前已经计算的值。通过增加缓存代理的方式,mult 函数可以继续专注于自身的职责——计算乘积,缓存的功能
是由代理对象实现的。
缓存代理可以用于 ajax 分页请求,缓存上一页的数据。
# 动态创建代理
先实现一个加法
// 加法
var plus = function () {
var res = 0;
for (var i = 0, l = arguments.length; i < l; i++) {
res = res + arguments[i];
}
return res;
};
通过传入高阶函数的方式,可以为各种计算方法创建缓存代理。这些计算方法被当作参数传入一个专门用于创建缓存代理的工厂中, 这样一来,我们就可以为乘法、加法创建缓存代理。
var createProxyFactory = function (fn) {
var cache = {};
return function () {
var args = Array.prototype.join.call(arguments, ',');
if (args in cache) {
return cache[args];
}
return (cache[args] = fn.apply(this, arguments));
};
};
var proxyMult = createProxyFactory(mult);
var proxyPlus = createProxyFactory(plus);
# 总结
在什么情况下使用代理呢?
- 在开销比较大的情况下做一些惰性加载,使用的时候在去加载
- 当不方便直接访问某个对象的时候,使用代理
- 需要做一些缓存的时候,使用代理
在 JavaScript 常用的是虚拟代理和缓存代理。