設計模式的定義:在面向對象軟件設計過程當中針對特定問題的簡潔而優雅的解決方案javascript
固然咱們能夠用一個通俗的說法:設計模式是解決某個特定場景下對某種問題的解決方案。所以,當咱們遇到合適的場景時,咱們可能會條件反射同樣天然而然想到符合這種場景的設計模式。java
好比,當系統中某個接口的結構已經沒法知足咱們如今的業務需求,但又不能改動這個接口,由於可能原來的系統不少功能都依賴於這個接口,改動接口會牽扯到太多文件。所以應對這種場景,咱們能夠很快地想到能夠用適配器模式來解決這個問題。算法
下面介紹幾種在JavaScript中常見的幾種設計模式:設計模式
單例模式的定義:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。實現的方法爲先判斷實例存在與否,若是存在則直接返回,若是不存在就建立了再返回,這就確保了一個類只有一個實例對象。app
適用場景:一個單一對象。好比:彈窗,不管點擊多少次,彈窗只應該被建立一次。異步
class CreateUser {
constructor(name) {
this.name = name;
this.getName();
}
getName() {
return this.name;
}
}
// 代理實現單例模式
var ProxyMode = (function() {
var instance = null;
return function(name) {
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
})();
// 測試單體模式的實例
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
// 由於單體模式是隻實例化一次,因此下面的實例是相等的
console.log(a === b); //true複製代碼
策略模式的定義:定義一系列的算法,把他們一個個封裝起來,而且使他們能夠相互替換。函數
策略模式的目的就是將算法的使用算法的實現分離開來。測試
一個基於策略模式的程序至少由兩部分組成。第一個部分是一組策略類(可變),策略類封裝了具體的算法,並負責具體的計算過程。第二個部分是環境類Context(不變),Context接受客戶的請求,隨後將請求委託給某一個策略類。要作到這一點,說明Context中要維持對某個策略對象的引用。ui
/*策略類*/
var levelOBJ = {
"A": function(money) {
return money * 4;
},
"B" : function(money) {
return money * 3;
},
"C" : function(money) {
return money * 2;
}
};
/*環境類*/
var calculateBouns =function(level,money) {
return levelOBJ[level](money);
};
console.log(calculateBouns('A',10000)); // 40000複製代碼
代理模式的定義:爲一個對象提供一個代用品或佔位符,以便控制對它的訪問。this
經常使用的虛擬代理形式:某一個花銷很大的操做,能夠經過虛擬代理的方式延遲到這種須要它的時候纔去建立(例:使用虛擬代理實現圖片懶加載)
圖片懶加載的方式:先經過一張loading圖佔位,而後經過異步的方式加載圖片,等圖片加載好了再把完成的圖片加載到img標籤裏面。
var imgFunc = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var proxyImage = (function() {
var img = new Image();
img.onload = function() {
imgFunc.setSrc(this.src);
}
return {
setSrc: function(src) {
imgFunc.setSrc('./loading,gif');
img.src = src;
}
}
})();
proxyImage.setSrc('./pic.png');複製代碼
使用代理模式實現圖片懶加載的優勢還有符合單一職責原則。減小一個類或方法的粒度和耦合度。
中介者模式的定義:經過一箇中介者對象,其餘全部的相關對象都經過該中介者對象來通訊,而不是相互引用,當其中的一個對象發生改變時,只須要通知中介者對象便可。經過中介者模式能夠解除對象與對象之間的緊耦合關係。
例如:現實生活中,航線上的飛機只須要和機場的塔臺通訊就能肯定航線和飛行狀態,而不須要和全部飛機通訊。同時塔臺做爲中介者,知道每架飛機的飛行狀態,因此能夠安排全部飛機的起降和航線安排。
中介者模式適用的場景:例如購物車需求,存在商品選擇表單、顏色選擇表單、購買數量表單等等,都會觸發change事件,那麼能夠經過中介者來轉發處理這些事件,實現各個事件間的解耦,僅僅維護中介者對象便可。
var goods = { //手機庫存
'red|32G': 3,
'red|64G': 1,
'blue|32G': 7,
'blue|32G': 6,
};
//中介者
var mediator = (function() {
var colorSelect = document.getElementById('colorSelect');
var memorySelect = document.getElementById('memorySelect');
var numSelect = document.getElementById('numSelect');
return {
changed: function(obj) {
switch(obj){
case colorSelect:
//TODO
break;
case memorySelect:
//TODO
break;
case numSelect:
//TODO
break;
}
}
}
})();
colorSelect.onchange = function() {
mediator.changed(this);
};
memorySelect.onchange = function() {
mediator.changed(this);
};
numSelect.onchange = function() {
mediator.changed(this);
};複製代碼
裝飾者模式的定義:在不改變對象自身的基礎上,在程序運行期間給對象動態地添加方法。
例如:現有4種型號的自行車分別被定義成一個單獨的類,若是給每輛自行車都加上前燈、尾燈、鈴鐺這3個配件,若是用類繼承的方式,須要建立4*3=12個子類。但若是經過裝飾者模式,只須要建立3個類。
裝飾者模式適用的場景:原有方法維持不變,在原有方法上再掛載其餘方法來知足現有需求;函數的解耦,將函數拆分紅多個可複用的函數,再將拆分出來的函數掛載到某個函數上,實現相同的效果但加強了複用性。
例:用AOP裝飾函數實現裝飾者模式
Function.prototype.before = function(beforefn) {
var self = this; //保存原函數引用
return function(){ //返回包含了原函數和新函數的 '代理函數'
beforefn.apply(this, arguments); //執行新函數,修正this
return self.apply(this,arguments); //執行原函數
}
}
Function.prototype.after = function(afterfn) {
var self = this;
return function(){
var ret = self.apply(this,arguments);
afterfn.apply(this, arguments);
return ret;
}
}
var func = function() {
console.log('2');
}
//func1和func3爲掛載函數
var func1 = function() {
console.log('1');
}
var func3 = function() {
console.log('3');
}
func = func.before(func1).after(func3);
func();複製代碼