七、代理模式(Proxy Pattern)
1.概念介绍
代理模式(Proxy Pattern) 为其他对象提供一种代理,来控制这个对象的访问,代理是在客户端和真实对象之间的介质。

简单的理解:如我们需要请明星来做广告,我们会先通过联系Ta的经纪人,谈好条件才会给明星签合同。
2.优缺点和应用场景
2.1优点
- 职责单一且清晰。
- 保护真实对象。
- 开闭原则,高拓展性。
2.2缺点
- 由于在客户端和真实对象间添加代理对象,导致请求处理速度变慢。
- 实现代理模式需要额外工作,有些代理模式实现起来非常复杂。
2.3应用场景
- 需要隐藏或保护某个类,则为这个类添加代理。
- 需要给不同访问者提供不同权限,则在代理类上做判断。
- 需要为某个类添加功能,如添加日志缓存等,我们可以在代理的类做添加,而不管去改原来封装好的类。
3.基本案例
这里我们以吃午饭问题来学习代理模式。通常情况下,我们会有两种方式解决午饭问题:“去餐厅吃”和“叫外卖”。
去餐厅吃的话,我们就是自己过去吃饭了呗,如果是叫外卖,我们就会通过外卖小哥来拿到午饭才能吃起来。
- 去餐厅吃(没有使用代理模式)
1. // 定义午饭类 参数 菜名
2. let Lunch = function(greens){
3.  this.greens = greens;
4. }
5. Lunch.prototype.getGreens = function(){
6.  return this.greens;
7. }
8. // 定义我这个对象
9. let leo = {
10.  buy: function(greens){
11.  console.log(`午饭吃${greens.getGreens()}`);
12.  }
13. }
14. // 去餐厅吃
15. leo.buy(new Lunch('青椒炒肉')); // 午饭吃青椒炒肉
• 叫外卖(有使用代理模式)
16. // 定义午饭类 参数 菜名
17. let Lunch = function(greens){
18.  this.greens = greens;
19. }
20. Lunch.prototype.getGreens = function(){
21.  return this.greens;
22. }
23. // 定义外卖小哥这个对象
24. let brother = {
25.  buy: function(lunch){
26.  leo.buy(lunch.getGreens());
27.  }
28. }
29. // 定义我这个对象
30. let leo = {
31.  buy: function(greens){
32.  console.log(`午饭吃${greens}`);
33.  }
34. }
35. // 叫外卖
36. brother.buy(new Lunch('青椒炒肉')); // 午饭吃青椒炒肉
并且外卖小哥还会帮我们做一些其他事,比如帮我们带瓶可乐,我们改造 brother和 leo这2个对象,再看看效果:
1. let brother = {
2.     buy: function(lunch){
3.         if(leo.needCola) leo.buyCola();
4.         leo.buy(lunch.getGreens());
5.     }
6. }
7. 
8. let leo = {
9.     needCola: true,
10.     buy: function(greens){
11.         console.log(`午饭吃${greens}`);
12.     },
13.     buyCola: function(){
14.         console.log(`顺手买瓶可乐!`);
15.     }
16. }
17. brother.buy(new Lunch('青椒炒肉'));
18. // 顺手买瓶可乐!
19. // 午饭吃青椒炒肉4.保护代理
还是借用 3.基本案例 的叫外卖的例子,我们现在要实现保护代理,而我们需要外卖小哥为了我们的身体健康,超过晚上9点,就不帮我们买可乐。
还是改造上面买可乐的 brother对象代码:
1. let brother = {
2.     buy: function(lunch){
3.         let nowDate = new Date();
4.         if(nowDate.getHours() >= 21){
5.             console.log('亲,这么晚不要喝可乐哟!');
6.         }else{
7.             if(leo.needCola) leo.buyCola();
8.             leo.buy(lunch.getGreens());
9.         }
10.     }
11. }
12. brother.buy(new Lunch('青椒炒肉'));
13. // 顺手买瓶可乐!
14. // 午饭吃青椒炒肉5.虚拟代理
虚拟代理能把一些开销大的对象,延迟到真正需要的时候才去创建和执行。
我们这里举个图片懒加载的例子: 这个案例参考自JS设计模式-代理模式.
1. // 图片加载
2. let ele = (function(){
3.  let node = document.createElement('img');
4.  document.body.appendChild(node);
5.  return{
6.  setSrc : function(src){
7.  node.src = src;
8.  }
9.  }
10. })()
11.
12. // 代理对象
13. let proxy = (function(){
14.  let img = new Image();
15.  img.onload = function(){
16.  ele.setSrc(this.src);
17.  }
18.  return {
19.  setSrc : function(src){
20.  img.src = src;
21.  ele.setSrc('loading.png');
22.  }
23.  }
24. })()
25.
26. proxy.setSrc('example.png');
6.缓存代理
缓存代理是将一些开销大的运算结果提供暂存功能,当下次计算时,参数和之前一直,则将缓存的结果返回:
这个案例参考自JS设计模式-代理模式.
1. //计算乘积
2. let mult = function(){
3.  let result = 1;
4.  for(let i = 0; i<arguments.length; i++){
5.  result *= arguments[i];
6.  }
7.  return result;
8. }
9.
10. // 缓存代理
11. let proxy = (function(){
12.  let cache = {};
13.  return function(){
14.  let args = Array.prototype.join.call(arguments, '',);
15.  if(args in cache){
16.  return cache[args];
17.  }
18.  return cache[args] = mult.apply(this,arguments);
19.  }
20. })();
参考资料
- 《JavaScript Patterns》












