『面試的底氣』—— 設計模式之代理模式(一)|8月更文挑戰

這是我參與8月更文挑戰的第11天,活動詳情查看:8月更文挑戰前端

前言

在面試高級前端時,每每會遇到一些關於設計模式的問題,每次都回答不太理想。恰逢8月更文挑戰的活動,準備用一個月時間好好理一下關於設計模式方面的知識點,給本身增長點面試的底氣。web

定義

代碼模式:爲一個對象提供一個代用品或佔位符,以便控制對它的訪問。面試

關鍵

代理模式的關鍵是,當程序不方便或者不知足須要去直接訪問一個對象的時候,提供一個替身 對象來控制對這個對象的訪問,程序實際上訪問的是替身對象。替身對象對請求作出一些處理之 後,再把請求轉交給本體對象。設計模式

JavaScript中的代理模式

代理模式的變體種類很是多,不必定都適合在JavaScript中使用,本文挑兩種在JavaScript開發中最經常使用的兩種代理來介紹,分別是虛擬代理和緩存代理。緩存

虛擬代理

虛擬代理會把一些開銷很大的對象,延遲到真正須要它的時候纔去建立。這裏用實現圖片預加載功能的例子來介紹虛擬代理。markdown

圖片預加載是一種經常使用的技術,若是直接給某個 img 標籤節點設置 src 屬性,因爲圖片過大或者網絡不佳,圖片的位置每每有段時間會是一片空白。常見的作法是先用一張 loading 圖片佔位,而後用異步的方式加載圖片,等圖片加載好了再把它填充到 img 節點裏,這種場景就很適合使用虛擬代理。網絡

先建立一個MyImage類,用來生成一個 img 節點並將插入 body 中。app

class MyImage {
  constructor() {
    this.img = document.createElement('img');
    document.body.appendChild(this.img);
  }
  setSrc(src) {
    this.img.src = src;
  }
}
複製代碼

執行const myImage = new MyImage()生成一個 img 節點並將插入 body 中,先在開發者工具中把網速調至Slow 3G,而後經過 執行myImage.setSrc('xxx')給該 img 節點設置 src 屬性,能夠看到,在圖片被加載好以前,頁面中有段時間會是一片空白。異步

爲了解決這個問題。寫一個代理類ProxyImage,經過這個代理類,實如今圖片被真正加載好以前,頁面中將出現一張 loading 圖片佔位, 來提示用戶圖片正在加載。工具

class ProxyImage {
  constructor() {
    this.myImage = new MyImage();
    this.image = new Image();
    this.image.onload = function () {
      this.myImage.setSrc(this.image.src);
    }
  }
  setSrc(src) {
    this.myImage.setSrc('./loading.gif');
    this.image.src = src;
  }
}
const proxyImage = new ProxyImage();
proxyImage.setSrc(xxxx);
複製代碼

經過ProxyImage代理類間接地訪問MyImage類實例化生成的對象,ProxyImage類控制了對MyImage類實例化生成的對象訪問,而且在此過程當中加入一些額外的操做,好比在真正的圖片加載好以前,先把 img 節點的 src 設置爲一張本地的 loading 圖片。

執行proxyImage.setSrc(xxxx)後,會先執行this.myImage.setSrc('./loading.gif')給 img 節點的src 屬性設置一張 loading 佔位圖的地址,同時把真正要展現的圖片地址 xxxx 賦值給new Image()建立的圖片對象的 src 屬性,在圖片對象加載成功後再把圖片地址 xxxx 經過this.myImage.setSrc(this.image.src)從新賦值給 img 節點的 src 屬性。

這樣在真正要展現的圖片加載成功以前,由MyImage類實例化生成的 img 節點會一直展現 loading 的佔位圖,實現圖片預加載的功能。

代理的意義

假如不用虛擬代理能夠實現圖片預加載的功能嗎,固然能夠,下面來實現一下:

class MyImage {
  constructor() {
    this.img = document.createElement('img');
    document.body.appendChild(this.img);
    this.image = new Image();
    image.onload = function () {
      this.img.src = this.image.src;
    };
  }
  setSrc(src) {
    this.img.src = './loading.gif';
    this.image.src = src;
  }
}
複製代碼

咋看一下代碼量還更少,代碼更簡單了。可是這麼寫違背了設計模式的單一職責原則,在MyImage類中除了負責生成 img 節點並設置src,還要負責預加載圖片。

假若有一張圖片很是小,根本不須要預加載,使用預加載還會出現佔位圖一閃而過的問題。那怎麼辦呢?難道要從新寫個MyImage類,這會違背開放—封閉原則

實際上MyImage類的功能只須要給 img 節點設置 src,預加載圖片只是一個錦上添花的功能。假如把這個功能放在代理中,那麼代理的做用在這裏就體現出來了,代理負責預加載圖片,預加載的操做完成以後,從新交給MyImage類中來給 img 節點設置 src 。

這裏並無改變或者增長 MyImage 的功能,只是經過代理,給MyImage類添加了新的功能。這是符合開放—封閉原則

給 img 節點設置 src 和圖片預加載這兩個功能,被隔離在兩個類裏,它們能夠各自變化而不影響對方。況且就算有一天咱們再也不須要預加載,那麼只須要改爲請求本體而不是請求代理對象便可。

相關文章
相關標籤/搜索