一、是什么

代理模式是为一个对象提供一个代用品或者占位符,以便获得对它的使用。
代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要时,提供一个替身对象来控制对象的实际操作,实际上访问的是替身对象。
在我们的生活中,代理模式是十分常见的。举个例子,当我们有租房、买房的需求时,更多的是去找链家等房屋中介机构,而不是直接去寻找想卖房或者出租房的人谈。此时,链家起到的作用就是代理的作用。

二、使用

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

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

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

  • 缓存代理
  • 虚拟代理

    缓存代理

    缓存代理可以为一些开销较大的运算提供暂时的存储,在下次运算时,如果传递进来的参数跟之前的一致,则可以直接返回前面存储的运算结果。
    如实现一个求积乘的函数,如下:
    1. const muti = function() {
    2. console.log('开始计算乘积');
    3. let a = 1;
    4. for (let i = 0, l = arguments.length; i < l; i++) {
    5. a = a * arguments[i];
    6. }
    7. return a;
    8. }
    现在加入缓存代理,如下: ```javascript var proxyMult = (function () { var cache = {}; return function () { var args = Array.prototype.join.call(arguments, “,”); if (args in cache) {
    1. return cache[args];
    } return (cache[args] = mult.apply(this, arguments)); }; })();

proxyMult(1, 2, 3, 4); // 输出:24 proxyMult(1, 2, 3, 4); // 输出:24

  1. 当第二次调用`proxyMult(1, 2, 3, 4)`时,本体`mult`函数并没有被计算,`proxyMult`直接返回了之前缓存好的计算结果。
  2. <a name="Y9Q88"></a>
  3. ### 虚拟代理
  4. 虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。<br />常见的就是图片预加载功能:
  5. - **未用代理模式如下:**
  6. ```javascript
  7. let myImage = (function() {
  8. let imageNode = document.createElement('img');
  9. document.body.appendChild(imgNode);
  10. // 创建一个Image对象,用于加载需要设置的图片
  11. let img = new Image;
  12. img.onload = function() {
  13. // 监听图片加载完成后,设置src为加载完成后的图片
  14. imgNode.src = img.src;
  15. }
  16. return {
  17. setSrc: function(src) {
  18. // 设置图片的时候,设置默认的 loading 图
  19. imgNode.src = 'https://cdn.nlark.com/yuque/0/2022/btqf.png';
  20. // 把真正需要设置的图片传给Image对象的src属性
  21. img.src = src;
  22. }
  23. }
  24. })();
  25. myImage.setSrc('https://xxx.png');

myImage对象除了负责给img节点设置src外,还要负责预加载图片,这违反了面向对象设计的原则 —— 单一职责原则。上述过程loading则是耦合进myImage对象里的,如果以后某个时候,我们不需要预加载显示loading这个功能了,就只能在myImage对象里面改动代码。

  • 使用代理模式 ```javascript // 图片本地对象,负责往页面中创建一个img标签,并且提供一个对外的setSrc接口 let myImage = (function(){ let imgNode = document.createElement( ‘img’ ); document.body.appendChild( imgNode );

    return {

    1. //setSrc接口,外界调用这个接口,便可以给该img标签设置src属性
    2. setSrc: function( src ){
    3. imgNode.src = src;
    4. }

    } })(); // 代理对象,负责图片预加载功能 let proxyImage = (function(){ // 创建一个Image对象,用于加载需要设置的图片 let img = new Image; img.onload = function(){

    1. // 监听到图片加载完成后,给被代理的图片本地对象设置src为加载完成后的图片
    2. myImage.setSrc( this.src );

    } return {

    1. setSrc: function( src ){
    2. // 设置图片时,在图片未被真正加载好时,以这张图作为loading,提示用户图片正在加载
    3. myImage.setSrc( 'https://cdn.nlark.com/yuque/0/2022/btqf.png' );
    4. img.src = src;
    5. }

    } })();

proxyImage.setSrc( ‘https://xxx.jpg‘ ); `` 使用代理模式后,图片本地对象负责往页面中创建一个img标签,并且提供一个对外的setSrc接口;代理对象负责在图片未加载完成之前,引入预加载的loading图,负责了图片预加载的功能。<br />上述并没有改变或者增加myImage的接口,但是通过代理对象,实际上给系统添加了新的行为。并且上述代理模式可以发现,代理和本体接口的一致性,如果有一天不需要预加载,那么就不需要代理对象,可以选择直接请求本体,其中关键的是代理对象和本体都对外提供了setSrc`方法。

三、应用场景

现在的很多前端框架或者状态管理框架都使用代理模式,用以监听变量的变化。
使用代理模式代理对象的访问的方式,一般又叫做拦截器,比如我们在项目中经常使用Axios的实例来进行 HTTP 的请求,使用拦截器interceptor可以提前对请求前的数据以及服务器返回的数据,进行一些预处理。
以及上述应用到的缓存代理和虚拟代理。