JavaScript設計模式之代理模式

定義

代理模式是爲對象提供一個代用品或者佔位符,以便控制對它的直接訪問。當一個對象不想被外界直接訪問,就能夠使用代理,提供一個替身對象來控制對它的訪問,替身對象對請求進行處理以後,再把請求轉交給本體對象。
javascript

proxy image1

不使用代理模式

使用代理模式

類型

通常代理有兩種類型,保護代理和虛擬代理。
保護代理 保護代理主要用來控制不一樣權限的對象對目標對象的訪問,若是目標對象想過濾掉一些請求,就可讓本身的代理去處理掉。
虛擬代理 虛擬代理主要用來延時去建立對象或者執行目標對象的方法,若是執行目標對象的方法是一個開銷很大的操做,虛擬代理就能夠先代理目標對象的方法,延遲到真正須要它的時候再去調用。

在JavaScript中實現保護代理比較困難,由於很難判斷誰訪問了對象,虛擬代理是一種比較經常使用的代理模式,因此接下來主要講解虛擬代理的應用。html

應用

使用代理實現圖片預加載

在web開發中,圖片預加載是一種經常使用的技術,當咱們在網絡環境差的狀況下加載一張大圖,圖片會有一段空白的時間,爲了提升用戶體驗,常見的作法是先使用一張loading佔位圖,而後經過異步的方式去加載圖片,等圖片加載好了再填充到img中。下面經過虛擬代理來實現這個功能:java

const myImage = (function () {
  const imgNode = document.createElement('img)
  document.body.appendChild(imgNode)
  return {
    setSrc: function (src) {
      imgNode.src = src
    }
  }
})()

const proxyImage = (function () {
  const img = new Image()
  img.onload = function () {
    myImage.setSrc(this.src)
  }
  return {
    setSrc: function (src) {
      myImage.setSrc('loading.gif')
      this.src = src
    }
  }
})()

proxyImage.setSrc('http://xxx.com/01.jpg')
複製代碼

思考
爲何這麼簡單的功能須要使用代理模式了?

咱們引入面向對象中的一個原則----單一職責原則
它指的是一個對象或者函數,應該只有一個引發它變化的緣由,通俗地將它只作一件事。在上述的圖片預加載功能中,若是咱們不引入代理,那myImage對象既要負責給img設置src,還要負責預加載圖片,這樣兩個功能耦合在一塊兒。若是有一天咱們不須要預加載功能,就只能直接修改myImage對象,把預加載圖片那部分的功能刪掉,這也違反了開閉原則。引入代理模式,完美瞭解決了上述的問題。程序員

使用虛擬代理合並HTTP請求

在web開發中,網絡請求的開銷很大,假設咱們要作一個文件同步的功能,當咱們選中文件的時候,文件就會同步到服務器。首先在頁面中建立可供點擊的checkbox節點:web

<input type="checkbox" id="1"/>1
<input type="checkbox" id="2"/>2
<input type="checkbox" id="3"/>3
<input type="checkbox" id="4"/>4
<input type="checkbox" id="5"/>5
<input type="checkbox" id="6"/>6
<input type="checkbox" id="7"/>7
<input type="checkbox" id="7"/>8
複製代碼

接下來,給checkbox綁定點擊事件,而後同步文件到服務器:ajax

const synchronousFile = function (id) {
  console.log(`同步文件, id爲${id}`)
}
const checkboxList = document.querySelectorAll('input')
for (let i = 0, len = checkboxList.length; i < len; i++) {
  const checkbox = checkboxList[i]
  checkbox.onclick = function () {
    if (this.checkbox === true) {
      synchronousFile(this.id)
    }
  }
}
複製代碼

當咱們一次性選了3個checkbox的時候,同時也向服務器發送了3次文件同步的請求。對於手速快的程序員,可能1秒鐘能夠點擊4個checkbox,這樣頻繁的網絡請求會帶來很大的開銷。
解決方案就是經過代理synchronousFile方法,收集一段時間的請求,最後一次性發給服務器。好比等待兩秒鐘以後,而後把兩秒鐘內的須要同步的文件列表一塊兒發送給服務器。實現代碼以下:後端

const synchronousFile = function (id) {
  console.log(`同步文件, id爲${id}`)
}

const proxySynchronousFile = (function () {
  let cache = [], timer;
  return function (id) {
    if (cache.indexOf(id) > -1) {
      cache.push(id)
    }
    if (timer) {
      return
    }
    timers = setTimeout(function () {
      synchronousFile(cache.join(','))
      clearTimeout(timer)
      timer = null
      cache = []
    }, 2000)
  }
})()

const checkboxList = document.querySelectorAll('input')
for (let i = 0, len = checkboxList.length; i < len; i++) {
  const checkbox = checkboxList[i]
  checkbox.onclick = function () {
    if (this.checkbox === true) {
      proxySynchronousFile(this.id)
    }
  }
}

複製代碼

緩存代理

緩存代理能夠爲一些開銷大的運算結果提供暫時的存儲,在下次運算時,若是傳遞進來的參數跟以前一致,則能夠直接返回結果。
計算階乘
緩存

const factorial = function (n) {
  if (n === 0 || n === 1) {
    return 1
  }
  return n * factorial(n-1)
}

factorial(4)   // 24
factorial(4)  // 24
複製代碼

引入代理:服務器

const proxyFactorial = (function () {
  const cache = {}
  return function (n) {
    if (cache[n]) {
      return cache[n]
    }
    return cache[n] = factorial.call(this, n)
  }
})()

proxyFactorial(4)  // 24
proxyFactorial(4)  // 24
複製代碼

緩存ajax請求
咱們在項目中常常會遇到分頁的請求,在一些場景下,同一頁的數據理論上只須要去服務端獲取一次,而後能夠緩存起來,下次再請求同一頁的時候就能夠直接從緩存中獲取。這種情形也能夠使用緩存代理,須要注意的是從後端獲取數據是一個異步操做,咱們沒法直接把計算結果同步的放在緩存中,而是要經過回調的方式。網絡

其它代理模式

代理模式的變體種類不少,下面作挑一些作簡單的介紹。

  • 防火牆代理:控制網絡資源的訪問,保護主機不讓「壞人接近」。
  • 遠程代理:爲一個對象在不一樣的地址空間提供局部表明。
  • 智能引用代理:取代了簡單的指針,它在訪問對象時執行一些附加操做,好比Vue中的對象劫持。
  • 寫時複製代理:一般用於複製一個龐大的的對象的狀況。這種代理模式延遲了複製的過程,當對象被真正修改時,纔對它進行復制操做。

總結

經過前面的這些應用咱們能夠看到,代理和目標對象有一致的接口。好比經過代理實現的預加載圖片的功能,實現了setSrc方法,當咱們有一天不須要圖片預加載功能,也能夠直接調用本體的方法,由於它們都對外提供了setSrc方法。在客戶看來,代理和目標對象是一致的,客戶並不知道代理和目標對象的區別。這樣的作有兩個好處:

  • 用戶能夠放心使用代理,由於他只關心是否能獲得想要的結果
  • 在任何使用本體的地方均可以替換成代理,不須要代理的時候,也能夠直接使用本體
相關文章
相關標籤/搜索