「這是我參與8月更文挑戰的第11天,活動詳情查看:8月更文挑戰」前端
在面試高級前端時,每每會遇到一些關於設計模式的問題,每次都回答不太理想。恰逢8月更文挑戰的活動,準備用一個月時間好好理一下關於設計模式方面的知識點,給本身增長點面試的底氣。web
代碼模式:爲一個對象提供一個代用品或佔位符,以便控制對它的訪問。面試
代理模式的關鍵是,當程序不方便或者不知足須要去直接訪問一個對象的時候,提供一個替身 對象來控制對這個對象的訪問,程序實際上訪問的是替身對象。替身對象對請求作出一些處理之 後,再把請求轉交給本體對象。設計模式
代理模式的變體種類很是多,不必定都適合在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 和圖片預加載這兩個功能,被隔離在兩個類裏,它們能夠各自變化而不影響對方。況且就算有一天咱們再也不須要預加載,那麼只須要改爲請求本體而不是請求代理對象便可。