Js设计模式 - 代理模式

代理代理,顾名思义就是找个人帮你做事情。为什么不自己做要找别人做呢?无非是这件事让别人可以达到成本最优。

寄快递 - 代理请求

假设你要送个包裹,从成都到北京。你是选择自己带着它去,还是交给快递公司呢?除非是十万火急,谁也不会亲自跑一趟吧?

那么来看这种情形,我们在前端有几十个按钮,点击每个按钮都会删除掉服务器上对应的某个文件。如果每次点击都向后台发送 DELETE 请求,那你每次点击就等同于亲自跑一趟。万一点击速度很快,网络开销就会非常大。

那怎么办呢?丢给快递公司吧。快递公司会先收集一天全部的货物,统一运送,进而减少运输成本。我们模仿快递公司的服务,收集几秒之内全部的请求,统一发出,减少网络开销。

// 快递公司服务
expressCom = (() => {  
  let timer = null
  let cache = []
  return ((buttons) => {
    if (!timer) {
      for (i = 0; i < buttons.length(); i++) {
        if (buttons[i].isDeleted) {
          cache[cache.length] = buttons[i]
        }
        // 组合执行网络请求
        // 每 5s 一次
        let timer = setTimeOut({
          fetch('url here with cache')
            .then('some actions here')

          timer = null
          cache = []
        }, 5000)
      }
    }
  })
})()

// 挑选出需要送的包裹
pickPkg = (() => {  
  let buttons = document.getElementByClassName('button')
  for (i = 0; i < buttons.length(); i++) {
    buttons[i].onClick = (() => { 
      this.isDeleted = true
      this.disabled = true

      // 交付快递公司
      expressCom(buttons)
    })
  }
})()

查手册 - 缓存代理

还记得指对数表手册吗?以前算一个指对数运算,查表无疑是最快的方式。同理,如果一个计算很费时间,算完之后就可以把它存进“手册”,下次再算的时候就不必那么麻烦了。这就是缓存代理。

// 手册核心逻辑
handBook = (equation) => {  
  let cache = {}
  return ((equation) => {
    // 为保持一致性去掉方程中的空格
    let pureEquation = equation.replace(/\s/g,"")
    if (pureEquation in cache) {
        return cache[pureEquation]
    }
    else {
      let result = null

      // 复杂的计算过程

      cache[pureEquation] = result
      return result
    }
  })
}()

// 不算了,直接查手册
calc = (equation) => {  
  handBook(equation)
}

跑腿服务 - 代理加载

我还有更重要的事要做,去你帮我跑个腿吧。将页面大体积资源的加载交给跑腿服务,避免阻塞后面的资源。或者在大图片加载出来之前先用 loading.png 代替。

<div id="imgBox"></div>  
// 跑腿服务逻辑
errand = (() => {  
  const errandBoy = new Image()
  return ((myImg, bigImageSrc) => {
    errandBoy.src = bigImageSrc

    // 跑腿完成移交委托者
    errandBoy.onload = () => {
      myImg.src = bigImageSrc
    }
  })
})()

// 创建委托者
const myImg = document.createNode('img')  
const box = document.getElementById('imgBox')  
imgBox.appendChild(myImg)

// 初始化委托者
// 委托者不请求大图资源
myImg.src = 'loading.png'

// 跑腿服务启动
errand(myImg, bigImageSrc)  

常见的三种代理模式,就说到这吧。不少内容参考了这篇文章