面试官:说说你对代理模式的理解?应用场景?

开发 前端
代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要时,提供一个替身对象来控制这个对象的访问,客户实际上访问的是替身对象。

[[433461]]

一、是什么

代理模式(Proxy Pattern)是为一个对象提供一个代用品或占位符,以便控制对它的访问

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要时,提供一个替身对象来控制这个对象的访问,客户实际上访问的是替身对象

在生活中,代理模式的场景是十分常见的,例如我们现在如果有租房、买房的需求,更多的是去找链家等房屋中介机构,而不是直接寻找想卖房或出租房的人谈。此时,链家起到的作用就是代理的作用

二、使用

在ES6中,存在proxy构建函数能够让我们轻松使用代理模式:

  1. const proxy = new Proxy(target, handler); 

关于Proxy的使用可以翻看以前的文章

而按照功能来划分,javascript代理模式常用的有:

  • 缓存代理
  • 虚拟代理

缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果

如实现一个求积乘的函数,如下:

  1. var muti = function () { 
  2.   console.log("开始计算乘积"); 
  3.   var a = 1; 
  4.   for (var i = 0, l = arguments.length; i < l; i++) { 
  5.     a = a * arguments[i]; 
  6.   } 
  7.   return a; 
  8. }; 

现在加入缓存代理,如下:

  1. var proxyMult = (function () { 
  2.   var cache = {}; 
  3.   return function () { 
  4.     var args = Array.prototype.join.call(arguments, ","); 
  5.     if (args in cache) { 
  6.       return cache[args]; 
  7.     } 
  8.     return (cache[args] = mult.apply(this, arguments)); 
  9.   }; 
  10. })(); 
  11.  
  12. proxyMult(1, 2, 3, 4); // 输出:24 
  13. proxyMult(1, 2, 3, 4); // 输出:24 

当第二次调用 proxyMult(1, 2, 3, 4) 时,本体 mult 函数并没有被计算,proxyMult 直接返回了之前缓存好的计算结果

虚拟代理

虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建

常见的就是图片预加载功能:

未使用代理模式如下:

  1. let MyImage = (function(){ 
  2.     let imgNode = document.createElement( 'img' ); 
  3.     document.body.appendChild( imgNode ); 
  4.     // 创建一个Image对象,用于加载需要设置的图片 
  5.     let img = new Image; 
  6.  
  7.     img.onload = function(){ 
  8.         // 监听到图片加载完成后,设置src为加载完成后的图片 
  9.         imgNode.src = img.src; 
  10.     }; 
  11.  
  12.     return { 
  13.         setSrc: function( src ){ 
  14.             // 设置图片的时候,设置为默认的loading图 
  15.             imgNode.src = 'https://img.zcool.cn/community/01deed576019060000018c1bd2352d.gif'
  16.             // 把真正需要设置的图片传给Image对象的src属性 
  17.             img.src = src; 
  18.         } 
  19.     } 
  20. })(); 
  21.  
  22. MyImage.setSrc( 'https://xxx.jpg' ); 

MyImage对象除了负责给img节点设置src外,还要负责预加载图片,违反了面向对象设计的原则——单一职责原则

上述过程loding则是耦合进MyImage对象里的,如果以后某个时候,我们不需要预加载显示loading这个功能了,就只能在MyImage对象里面改动代码

使用代理模式,代码则如下:

  1. // 图片本地对象,负责往页面中创建一个img标签,并且提供一个对外的setSrc接口 
  2. let myImage = (function(){ 
  3.     let imgNode = document.createElement( 'img' ); 
  4.     document.body.appendChild( imgNode ); 
  5.  
  6.     return { 
  7.         //setSrc接口,外界调用这个接口,便可以给该img标签设置src属性 
  8.         setSrc: function( src ){ 
  9.             imgNode.src = src; 
  10.         } 
  11.     } 
  12. })(); 
  13. // 代理对象,负责图片预加载功能 
  14. let proxyImage = (function(){ 
  15.     // 创建一个Image对象,用于加载需要设置的图片 
  16.     let img = new Image; 
  17.     img.onload = function(){ 
  18.         // 监听到图片加载完成后,给被代理的图片本地对象设置src为加载完成后的图片 
  19.         myImage.setSrc( this.src ); 
  20.     } 
  21.     return { 
  22.         setSrc: function( src ){ 
  23.             // 设置图片时,在图片未被真正加载好时,以这张图作为loading,提示用户图片正在加载 
  24.             myImage.setSrc( 'https://img.zcool.cn/community/01deed576019060000018c1bd2352d.gif' ); 
  25.             img.src = src; 
  26.         } 
  27.     } 
  28. })(); 
  29.  
  30. proxyImage.setSrc( 'https://xxx.jpg' ); 

使用代理模式后,图片本地对象负责往页面中创建一个img标签,并且提供一个对外的setSrc接口;

代理对象负责在图片未加载完成之前,引入预加载的loading图,负责了图片预加载的功能

上述并没有改变或者增加MyImage的接口,但是通过代理对象,实际上给系统添加了新的行为

并且上述代理模式可以发现,代理和本体接口的一致性,如果有一天不需要预加载,那么就不需要代理对象,可以选择直接请求本体。其中关键是代理对象和本体都对外提供了 setSrc 方法

三、应用场景

现在的很多前端框架或者状态管理框架都使用代理模式,用于监听变量的变化

使用代理模式代理对象的访问的方式,一般又被称为拦截器,比如我们在项目中经常使用 Axios 的实例来进行 HTTP 的请求,使用拦截器 interceptor 可以提前对 请求前的数据 服务器返回的数据进行一些预处理

以及上述应用到的缓存代理和虚拟代理

参考文献

https://juejin.cn/post/6844903555036364814#heading-2

https://juejin.cn/post/6992510837403418654#heading-7

 

https://sothx.com/2021/06/26/proxy/

 

责任编辑:武晓燕 来源: JS每日一题
相关推荐

2021-11-10 07:47:49

组合模式场景

2021-11-03 14:10:28

工厂模式场景

2021-11-09 08:51:13

模式命令面试

2021-11-22 23:50:59

责任链模式场景

2021-11-11 16:37:05

模板模式方法

2021-09-16 07:52:18

算法应用场景

2021-08-16 08:33:26

git

2021-11-04 06:58:32

策略模式面试

2021-09-29 07:24:20

场景数据

2021-09-28 07:12:09

测试路径

2021-09-06 10:51:27

TypeScriptJavaScript

2021-09-08 07:49:34

TypeScript 泛型场景

2021-10-09 10:25:41

排序应用场景

2021-09-10 06:50:03

TypeScript装饰器应用

2021-10-08 09:59:32

冒泡排序场景

2021-10-13 18:01:33

快速排序场景

2021-05-31 10:35:34

TCPWebSocket协议

2021-10-11 09:38:41

开源

2021-10-12 07:15:02

归并排序场景

2021-10-18 07:51:39

回溯算法面试
点赞
收藏

51CTO技术栈公众号