代理模式
定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。
代理对象和本体对象实现了同样的接口,并且会把任何方法调用传递给本体对象;
举例:
图片预加载、图片懒加载、
合并HTTP请求(代理收集一定时间内的所有HTTP请求,然后一次性发给服务器)、
惰性加载(通过代理处理和收集一些基本操作,然后仅在真正需要本体的时候才加载本体)、
缓存代理(缓存请求结果、计算结果)
缓存代理
缓存代理可以作为一些开销大的运算结果提供暂时的存储,下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33// 先实现具体的两个算法
const mult = function() {
let a = 1;
for (let i = 0; i < arguments.length; i++) {
a *= arguments[i];
}
return a;
};
const plus = function() {
let a = 0;
for (let i = 0; i < arguments.length; i++) {
a += arguments[i];
}
return a;
};
// 创建缓存代理
const createProxyFactory = function(fn) {
let cache = {}; // 保存计算的结果
// 使用闭包在内存中保留对cache的引用
return function() {
let args = Array.from(arguments).join(','); // 将所有参数转化为字符串作为缓存的 key
if (args in cache) {
return cache[args];
} else {
return cache[args] = fn.apply(this, arguments);
}
};
};
// 使用代理对象
const proxyMult = createProxyFactory(mult);
const proxyPlus = createProxyFactory(plus);
console.log(proxyMult(1,2,3,4)); // 24
console.log(proxyPlus(1,2,3,4)); // 10
虚拟代理
虚拟代理:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建
例:使用虚拟代理实现图片懒加载1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64// 这里是html的代码,
<ul>
<li><img src="./img/loading.gif" data-url="./img/1.jpg"/></li>
<li><img src="./img/loading.gif" data-url="./img/2.jpg"/></li>
<li><img src="./img/loading.gif" data-url="./img/3.jpg"/></li>
<li><img src="./img/loading.gif" data-url="./img/4.jpg"/></li>
<li><img src="./img/loading.gif" data-url="./img/5.jpg"/></li>
<li><img src="./img/loading.gif" data-url="./img/6.jpg"/></li>
</ul>// ...省略很多的图片
// 这里开始是js代码,没有写script,只是简单的模拟
function $(t){
return document.getElementsByTagName(t);
}
// 获取id
function lazyLoad(el) {
// 遍历需要加载的图片元素
for (var i = 0, len = el.length; i < len; i++) {
// 根据class属性判断是否已经加载过
if (el[i].className != "load") {
// 这里就是获取data-url的值,也就是真实图片路径,替代loading图片
el[i].src = el[i].getAttribute("data-url");
// 替代后加一个class属性已经加载过的标记
el[i].className = "load";
}
}
};
// 滚动时执行
window.onscroll = function() {
lazyLoad($("img"));
};
// 加载后执行
window.onload = function() {
lazyLoad($("img"));
};
-------------------------------------------------------------
// 本体对象
const imgFunc = (function() {
const imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc(src){
imgNode.src = src;
}
}
})();
// 代理对象
const proxyImage = (function() {
const img = new Image();
img.onload = function() {
imgFunc.setSrc(this.src);
};
return {
setSrc(src){
imgFunc.setSrc('./loading.gif');
img.src = src;
}
};
})();
// 使用代理对象
proxyImage.setSrc('./reality.png');
图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。
这里讲述一下代理对象做了那些事:
1.创建了一个 Image 对象,并为其绑定了 onload 事件。
2.将 imgNode 先设置为 ‘./loading.gif’ 加载的菊花图。
3.当 Image 对象加载完真实的图片,也就是上文的 ‘./reality.png’ ,将 imgNode 设置为 ‘./reality.png’。
合并http请求
如果有一个功能需要频繁进行请求操作,这样开销比较大,可以通过一个代理函数收集一段时间内请求数据,一次性发出1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//上传请求
let upload = function(ids){
$.ajax({
data: {
id:ids
}
})
}
//代理合并请求
let proxy = (function(){
let cache = [],
timer = null;
return function(id){
cache[cache.length] = id;
if(timer) return false;
timer = setTimeout(function(){
upload(cache.join(','));
clearTimeout(timer);
timer = null;
cache = [];
},2000);
}
})();
// 绑定点击事件
let checkbox = document.getElementsByTagName( "input" );
for(var i= 0, c; c = checkbox[i++];){
c.onclick = function(){
if(this.checked === true){
proxy(this.id);
}
}
}
代理模式优缺点
- 优点:代理模式能将代理对象与被调用对象分离,降低了系统的耦合度。代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。代理对象也可以对目标对象调用之前进行其他操作。
- 缺点:增加了系统的复杂度