原文連接: JavaScript經常使用設計模式
設計模式是一種在長時間的經驗與錯誤中總結出來可服用的解決方案。javascript
建立型設計模式:專一於處理對象的建立html
Constructor構造器模式,Factory工廠模式,Singleton單例模式,builder生成器模式
結構型設計模式:對象間組合,創建對象之間的關係java
Decorator裝飾者模式,Facade外觀模式,Flyweight享元模式,Adapter適配器模式,Proxy代理模式
行爲設計模式:簡化和改善對象間的通訊git
Mediator中介者模式,Observer觀察者模式
一個目標對象維持着一系列依賴於它的對象,將有關狀態的任何變動自動通知觀察者們。在觀察者模式中,觀察者須要直接訂閱目標對象,觀察者與目標對象之間有必定的依賴關係。
有4個重要的概念github
// 目標對象 class Subject { constructor() { // 觀察者列表 this.observers = [] } addObserver(observer) { this.observers.push(observer) } removeObserver() { this.observers.pop() } notify() { this.observers.forEach(observer => { observer.update() }) } } // 觀察者 class Observer { constructor() { // 使用時會被具體update方法覆蓋 this.update = function () { // .. } } }
// 具體目標對象 class currentSubject extends Subject { constructor() { super() } // 其餘自定義方法 dosomething() { console.log('currentSubject change') this.notify() } } // 具體觀察者 class currentObserver extends Observer { constructor() { super() } // 重寫update update() { console.log('change!') } }
// 訂閱 let curSubject = new currentSubject() let curObserver = new currentObserver() curSubject.addObserver(curObserver) // 觸發 curSubject.dosomething() // currentSubject change
發佈訂閱模式能夠說是觀察這模式的一種變體,一種實現。它使用一個主題/事件通道,介於發佈者和訂閱者之間,避免了發佈者和訂閱者之間的依賴關係。segmentfault
class PubSub { constructor() { // 主題/事件通道 this.topics = {} } publish(topic, args) { if (!this.topics[topic]) { return } let subscribers = this.topics[topic] subscribers.forEach(subscriber => { subscriber.updata(args) }) } subscribe(topic, subscriber ) { if (!this.topics[topic]) { this.topics[topic] = [] } this.topics[topic].push(subscriber ) } } let pubsub = new PubSub() pubsub.subscribe('one', subscriber ) pubsub.publish('one', 'some args')
工廠函數提供一個通用的接口來建立對象,咱們能夠指定咱們但願建立的對象類型,咱們通知工廠函數須要什麼類型的對象並提供對應的數據,返回對應的實例。設計模式
class Car { constructor(options) { this.doors = options.doors || 4; this.state = options.state || "brand new"; this.color = options.color || "silver"; } } class Truck { constructor(options) { this.state = options.state || "used"; this.wheelSize = options.wheelSize || "large"; this.color = options.color || "blue"; } } function vehicleFactory (options) { if (options.type === 'car') { return new Car(options) } else { return new Truck(options) } }
什麼時候使用工廠模式api
缺點閉包
抽象工廠模式,將對象的實現細節抽離出來。適用於須要和多種對象一塊兒工做的場景。函數
class Truck { constructor(options) { this.state = options.state || "used"; this.wheelSize = options.wheelSize || "large"; this.color = options.color || "blue"; } } class Car { constructor(options) { this.doors = options.doors || 4; this.state = options.state || "brand new"; this.color = options.color || "silver"; } } class AbstractFactory { constructor() { this.types = {} } registerFactory(type, factory) { this.types[type] = factory } getInstance(type, args) { let factory = this.types[type] if (factory) { return new factory(args) } } } let abstractFactory = new AbortController() abstractFactory.registerFactory('car', Car) abstractFactory.registerFactory('truck', Truck) abstractFactory.getInstance('car', options) abstractFactory.getInstance('truck', options)
單例模式限制一個類只有一個實例化對象。
class Obj(data) { // .... } // 利用閉包實現單例模式,確保obj類只有一個實例 function singleton (data) { var instance; return function () { if (!instance) { instance = new Obj(data) } return instance } }
中介者模式就是提供一箇中心點給系統不一樣組件之間進行通訊,下降系統組件之間的耦合程度。
// 實現與發佈/訂閱模式相似
觀察者模式和發佈訂閱模式專一於維護目標對象和觀察者之間的關係,當主題對象發送變化時,通知全部對改主題感興趣的觀察者。而中介者模式的話,專一於限制對象的通訊必須經過中介者來通訊。二者都提倡鬆耦合。
裝飾者模式,經過一個裝飾類對現有動態添加行爲,以及對原有行爲進行裝飾。
// o爲已有對象 var M20 = function(o){ // 這裏定義一個裝飾類 var str = '20多歲的時候,'; // o是傳入的對象,調用傳入對象的方法,加以裝飾 this.eat = function(){ return str + o.eat()+",肥得很!"; }; this.drink = function(){ return str + o.drink()+",就是個水桶!"; }; this.coding = function(){ return str + o.coding()+",代碼又寫得撇!"; }; } alert(new M20(david).eat()); // 20多歲的時候,大衛是個大胖子,一天只曉得吃,肥得很! alert(new M20(david).drink()); // 20多歲的時候,大衛除了吃就是喝,就是個水桶! alert(new M20(david).coding()); // 20多歲的時候,寫代碼吧,大衛,代碼又寫得撇!
使用一個新的接口對現有的接口進行包裝,處理數據與接口的不匹配。
function api (x1, x2, x3) { console.log(x1 + x2 + x3); // 用console.log來模擬接口的相關操做 } var data = { a: '我', b: '很', c: '帥' } function adapterApi (o) { // 經過適配器函數來調用目的api api(o.a, o.b, o.c); } adapterApi(data); // 我很帥