代理模式是一种对程序对象进行控制性访问的一类解决方案。
引入代理模式,其实是为了实现单一职责的面向对象设计原则。
单一职责其实就是指在一个类中(js中通常指对象和函数等),应仅有一个引起它变化的原因。这样会帮助程序设计具有良好的健壮和高内聚特性,从而当变化发生时,程序设计会尽量少的受到意外破坏。
代理模式有多种方法,保护代理、远程代理、虚拟代理、缓存代理等。
但在javascript
中,代理模式最常用到的两种方法是虚拟代理和缓存代理。
虚拟代理
在理解虚拟代理时,可以将其想象为一个经纪人,客户程序需要通过这个虚拟代理(经纪人)来调用本体对象的方法。
虚拟代理示例demo1: 图片loading预加载
1//通过虚拟代理实现图片预加载
2
3//代理模式进行图片预加载的实现思路是: 通过代理对象获取实际显示图片地址并进行加载,同时先让本体对象显示预加载图片,待代理对象将实际图片地址加载完毕后传递给本体对象进行显示即可。
4
5//本体对象
6var myImage = (function(){
7 var imgNode = new Image()
8 document.body.appendChild(imgNode)
9
10 return {
11 setSrc: function(src){
12 imgNode.src = src
13 }
14 }
15})()
16
17//代理对象
18var proxyImage = (function(){
19 var img = new Image(); //1、代理对象新建一个img对象
20 img.onload = function(){ //4、代理对象img加载真实图片src完成后将src传递给本体对象显示
21 myImage.setSrc(this.src)
22 }
23 return {
24 setProxySrc: function(src){
25 myImage.setSrc('../images/loding.gif') //2、代理对象控制本体对象使用加载图片src
26 img.src = src //3、代理对象的img对象获取将要传递给本体对象的真实图片src
27 }
28 }
29})()
30
31//通过代理对象来对本体对象进行访问
32proxyImage.setProxySrc('https://p1.ssl.qhimgs1.com/t0153297036f4471d81.jpg')
虚拟代理示例demo2:合并HTTP请求,减少网络请求资源消耗
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<meta http-equiv="X-UA-Compatible" content="ie=edge">
7<title>代理模式-虚拟代理合并HTTP请求</title>
8</head>
9<body>
10<div>
11 <input type="checkbox" id="1" />1
12 <input type="checkbox" id="2" />2
13 <input type="checkbox" id="3" />3
14 <input type="checkbox" id="4" />4
15 <input type="checkbox" id="5" />5
16 <input type="checkbox" id="6" />6
17 <input type="checkbox" id="7" />7
18 <input type="checkbox" id="8" />8
19 <input type="checkbox" id="9" />9
20</div>
21</body>
22<script>
23var synchronousFile = function(id){
24 console.log('开始同步:' + id);
25}
26
27var proxySynchronousFile = (function(){
28 var cache = [], //保存一段时间内需要同步的id
29 timer; //定时器
30
31 //闭包函数
32 return {
33 proxySyncFile: function(id){
34 cache.push(id)
35
36 if(timer){ //保证不会覆盖已经启动的定时器
37 return
38 }
39
40 timer = setTimeout(function(){
41 synchronousFile(cache.join(',')) //2秒后向本体对象发送需要同步的ID集合
42 clearTimeout(timer) //清空定时器
43 timer = null
44 cache.length = 0 //清空id集合
45 }, 2000)
46 }
47 }
48})()
49
50var check = document.getElementsByTagName('input')
51for(var i=0; i<check.length; i++){
52 check[i].onclick = function(){
53 proxySynchronousFile.proxySyncFile(this.id)
54 }
55}
56</script>
57</html>
缓存代理
缓存代理可以为一些开销大的运算结果提供暂时存储,在下次运算时,如果传递进来的参数和之前的一致,则可以直接返回前面存储的结果
缓存代理示例demo: 计算乘积
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<meta http-equiv="X-UA-Compatible" content="ie=edge">
7<title>代理模式-缓存代理</title>
8</head>
9<body>
10<input type="text" id="input1">*
11<input type="text" id="input2">
12<div id="result"></div>
13<button type="button" id="btn">计算</button>
14</body>
15<script>
16
17//缓存代理示例: 计算乘积
18//本体对象
19var calculate = function(){
20 var a = 1;
21 for(var i=0; i<arguments.length; i++){
22 a = a*arguments[i]
23 }
24 return a;
25}
26
27//代理对象,创建缓存代理的工厂,参数fn可以为任意需要进行代理的函数,除了上述计算乘积的本体对象函数外,还可以是计算加减或进行其他操作的本体函数
28var proxyCalculate = function(fn){
29 var resultCache = {};
30
31 return function(){
32 var args = Array.prototype.join.call(arguments, ',')
33 if(args in resultCache){ //测试对象中是否有对应的name,有则直接返回该name的值
34 return resultCache[args]
35 }
36 return resultCache[args] = fn.apply(this, arguments)
37 }
38}
39
40
41document.getElementById('btn').onclick = function(){
42 var v1 = document.getElementById('input1').value
43 var v2 = document.getElementById('input2').value
44 var result = proxyCalculate(calculate)(v1, v2)
45
46 document.getElementById('result').innerHTML = result
47}
48
49//总结: 代理模式还有多种,比如保护代理、远程代理等,但js中常用的代理模式有虚拟代理和缓存代理两种。
50</script>
51</html>
在编写业务代码时,并不需要一开始就考虑是否使用代理模式,只要当发现使用代理模式更方便时,再编写代理对象即可。