js中的设计模式之代理模式

代理模式

定义

为我们访问的对象提供一个中间对象(代理)以控制对这个对象的访问。

详细描述

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。就比如A 对象本来想要直接访问B对象,但是B对象觉得不太方便,所以找了个中介者C(代理),以后所有的都要通过C对象去访问B对象,关系如下图:
在这里插入图片描述

生活中也有很多代理模式的例子,就如上图中最下方,我们在租房市场中的关系,有些房东想要出租房子为了省心省事不想一个一个自己去和客户对接,所以找了一个中介(也就是代理),我们找房子的时候,都是需要通过中介然后中介在去联系房东,这种模式就叫做代理模式。根据代理的行为来说又大概分为保护代理、虚拟代理、缓存代理等。

  • 保护代理。代理帮助目标对象过滤掉一些请求。比如房东对顾客有要求比如养宠物的不租,不洗脚的不租,这个时候中介就会过滤掉这类型的租房者,这就是保护代理模式。
  • 虚拟代理。把一些开销很大的对象,延迟到真正需要它的时候才去创建。比如房东在外地来一趟很不容易,那么中介就将所有的事情先办好,等到最后签合同的时候,才给房东说你来吧来签合同,从而省去每次有事都需要房东亲自从外地大老远回来。
  • 缓存代理。为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果。比如我们租房的时候经常会问各种问题物业费谁交呀,电费怎么收啊等等,第一次问的时候需要中介联系房东来回答,但是以后在遇到同样的问题中介就不会在去联系房东,而是将房东之前的答案回答给租房者。
    在js中最常用的就是虚拟代理和缓存代理,下面我们就来看一下这两种代理的实例。

虚拟代理

把一些开销很大的对象,延迟到真正需要它的时候才去创建。
在实际业务中有时候直接加载较大图片时可能会造成页面空白,所以为了优化体验采用图片预加载的方式来优化(不知道图片预加载原理的可以看下这篇没有使用代理实现图片预加载的文章js实现图片预加载,下面我们就来看看虚拟代理在图片预加载技术中的应用,代码如下:

    
   const imgSet = (() => { // 目标对象
      const imgNode = document.createElement('img');
      document.body.appendChild(imgNode);
      
      return (url) => imgNode.src = url;
    })();

    const proxyImgSet = (() => { // 代理对象
      const img = new Image();
      img.onload = () => imgSet(img.src); // 使目标节点加载已经缓存好的图片

      return (url) => {
        // 加载目标对象前,代理增加一些自己的行为
        imgSet('./images/ef9081ecc65482ed7bcfc9b7e0b548d6.gif');
        img.src = url;
      }
    })();

    proxyImgSet('https://img1.baidu.com/it/u=3615107494,579574018&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500');
    

上述代码没有对目标对象直接加载图片,因为此时对它来说加载较大的图片开销较大,所以通过代理先设置一张小的占位图,同时进行异步缓存较大的图片,等图片缓存完成的时候代理对象在通知目标对象直接加载缓存里面的图片。代理在中间增加了自己的行为,使体验更友好。

缓存代理

为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果。
有些业务场景下会有比较复杂的数据处理或者计算,如果传入的参数都相同再次计算就会浪费性能,所以我们就把之前的计算做一个缓存,如果传入的参数相同,就之前返回上一次计算的值以节省开销。类似的思想在很多库中都有使用,如reselect库和vue中的computed计算属性中。下面看下使用代理实现为计算规则创建缓存的实例:

    // 乘积计算处理
    const mult = (...list) => {
      console.log('我乘积计算处理')
      let num = 1;
      list.forEach(item => num = num * item)
      return num;
    }

    // 和计算处理
    const add = (...list) => {
      console.log('我是和计算处理');
      let num = 0;
      list.forEach(item => num += item);
      return num;
    }

    // 创建缓存的代理
    const createProxy = (fn) => {
      // 闭包形成一个缓存对象
      const cacheObj = {};

      return function(...list) {
        const args = list.join('-');
        if (cacheObj[args]) return cacheObj[args];
        return cacheObj[args] = fn.apply(this, list);
      }
    }

    // 使用代理创建函数
    const proxyMult = createProxy( mult ),
      proxyPlus = createProxy( add );

  
    console.log( proxyMult( 1, 2, 3, 4 ) ); // 24
    console.log( proxyMult( 1, 2, 3, 4 ) ); // 24

    console.log( proxyPlus( 1, 2, 3, 4 ) ); // 10
    console.log( proxyPlus( 1, 2, 3, 4 ) ); // 10 

上述代码首先定义了两个计算规则mult 和add ,然后创建createProxy代理函数来调用计算函数,代理函数内部是会用闭包来缓存每次计算的结果,当结果相同时就不会在重复计算由代理函数负责直接返回之前的结果,计算函数只会执行一次。并且如果我们不想使用代理也可以直接调用原来的计算函数,不用做其他任何的修改。下面看下控制台上的打印结果:
在这里插入图片描述

总结

在 JavaScript 开发中最常用的是虚拟代理和缓存代理。这些业务不适用代理模式思想一样可以进行实现,但是使用代理也有更多的优点:

  • 更符合程序设计单一职责原则。如果一个对象承担的职责过多, 就会造成高耦合低内 聚的缺陷,非常不利于改动和扩展。
  • 代理和本体接口一致性。非常易于代理和本体使用的切换,并且不需要做其他多余的修改。
  • 更符合程序设计开放—封闭原则。我们并没有改变目标对象,却对它的行为进行了处理 和加工,更加方便的进行逻辑和性能优化的处理。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值