這種設計模式能夠大大下降程序模塊之間的耦合度,便於更加靈活的擴展和維護。html
// 一個播放器類 class Player { constructor() { // 初始化觀察者列表 this.watchers = {} // 模擬2秒後發佈一個'play'事件 setTimeout(() => { this._publish('play', true) }, 2000) // 模擬4秒後發佈一個'pause'事件 setTimeout(() => { this._publish('pause', true) }, 4000) } // 發佈事件 _publish(event, data) { if (this.watchers[event] && this.watchers[event].length) { this.watchers[event].forEach(callback => callback.bind(this)(data)) } } // 訂閱事件 subscribe(event, callback) { this.watchers[event] = this.watchers[event] || [] this.watchers[event].push(callback) } // 退訂事件 unsubscribe(event = null, callback = null) { // 若是傳入指定事件函數,則僅退訂此事件函數 if (callback&&event) { if (this.watchers[event] && this.watchers[event].length) { this.watchers[event].splice(this.watchers[event].findIndex(cb => Object.is(cb, callback)), 1) } // 若是僅傳入事件名稱,則退訂此事件對應的全部的事件函數 } else if (event) { this.watchers[event] = [] // 若是未傳入任何參數,則退訂全部事件 } else { this.watchers = {} } } } // 實例化播放器 const player = new Player() console.log(player) // 播放事件回調函數1 const onPlayerPlay1 = function(data) { console.log('1: Player is play, the `this` context is current player', this, data) } // 播放事件回調函數2 const onPlayerPlay2 = data => { console.log('2: Player is play', data) } // 暫停事件回調函數 const onPlayerPause = data => { console.log('Player is pause', data) } // 加載事件回調函數 const onPlayerLoaded = data => { console.log('Player is loaded', data) } // 可訂閱多個不一樣事件 player.subscribe('play', onPlayerPlay1) player.subscribe('play', onPlayerPlay2) player.subscribe('pause', onPlayerPause) player.subscribe('loaded', onPlayerLoaded) // 能夠退訂指定訂閱事件 player.unsubscribe('play', onPlayerPlay2) // 退訂指定事件名稱下的全部訂閱事件 player.unsubscribe('play') // 退訂全部訂閱事件 player.unsubscribe() // 能夠在外部手動發出事件(真實生產場景中,發佈特性通常爲類內部私有方法) player._publish('loaded', true)
觀察者模式經過維護一堆列表來管理對象間的多對多關係,中介者模式經過統一接口來維護一對多關係,且通訊者之間不須要知道彼此之間的關係,只須要約定好API便可。設計模式
// 汽車 class Bus { constructor() { // 初始化全部乘客 this.passengers = {} } // 發佈廣播 broadcast(passenger, message = passenger) { // 若是車上有乘客 if (Object.keys(this.passengers).length) { // 若是是針對某個乘客發的,就單獨給他聽 if (passenger.id && passenger.listen) { // 乘客他愛聽不聽 if (this.passengers[passenger.id]) { this.passengers[passenger.id].listen(message) } // 否則就廣播給全部乘客 } else { Object.keys(this.passengers).forEach(passenger => { if (this.passengers[passenger].listen) { this.passengers[passenger].listen(message) } }) } } } // 乘客上車 aboard(passenger) { this.passengers[passenger.id] = passenger } // 乘客下車 debus(passenger) { this.passengers[passenger.id] = null delete this.passengers[passenger.id] console.log(`乘客${passenger.id}下車`) } // 開車 start() { this.broadcast({ type: 1, content: '前方無障礙,開車!Over'}) } // 停車 end() { this.broadcast({ type: 2, content: '老司機翻車,停車!Over'}) } } // 乘客 class Passenger { constructor(id) { this.id = id } // 聽廣播 listen(message) { console.log(`乘客${this.id}收到消息`, message) // 乘客發現停車了,因而本身下車 if (Object.is(message.type, 2)) { this.debus() } } // 下車 debus() { console.log(`我是乘客${this.id},我如今要下車`, bus) bus.debus(this) } } // 建立一輛汽車 const bus = new Bus() // 建立兩個乘客 const passenger1 = new Passenger(1) const passenger2 = new Passenger(2) // 倆乘客分別上車 bus.aboard(passenger1) bus.aboard(passenger2) // 2秒後開車 setTimeout(bus.start.bind(bus), 2000) // 3秒時司機發現2號乘客沒買票,2號乘客被驅逐下車 setTimeout(() => { bus.broadcast(passenger2, { type: 3, content: '同志你好,你沒買票,請下車!' }) bus.debus(passenger2) }, 3000) // 4秒後到站停車 setTimeout(bus.end.bind(bus), 3600) // 6秒後再開車,車上已經沒乘客了 setTimeout(bus.start.bind(bus), 6666)
爲其餘對象提供一種代理以控制對這個對象的訪問。
代理模式使得代理對象控制具體對象的引用。代理幾乎能夠是任何對象:文件,資源,內存中的對象,或者是一些難以複製的東西。閉包
ES6中的Proxy對象app
const target = {} const handler = { get(target, property) { if (property in target) { return target[property] } else { throw new ReferenceError("Property \"" + property + "\" does not exist.") } } } const p = new Proxy(target, {}) p.a = 3 // 被轉發到代理的操做 console.log(p.c) //
保證一個類只有一個實例,並提供一個訪問它的全局訪問點(調用一個類,任什麼時候候返回的都是同一個實例)。函數
實現方法:使用一個變量來標誌當前是否已經爲某個類建立過對象,若是建立了,則在下一次獲取該類的實例時,直接返回以前建立的對象,不然就建立一個對象。測試
// 類數實例: class Singleton { constructor(name) { this.name = name this.instance = null // } getName() { alert(this.name) } static getInstance(name) { if (!this.instance) { this.instance = new Singleton(name) } return this.instance } } const ins = new Singleton('hhhh') const instanceA = Singleton.getInstance('seven1') const instanceB = Singleton.getInstance('seven2') //閉包包裝實例: const SingletonP = (function() { let instance return class Singleton { constructor(name) { if (instance) { return instance } else { this.init(name) instance = this return this } } init(name) { this.name = name console.log('已初始化') } } })() const instanceA = new SingletonP('seven1') const instanceB = new SingletonP('seven2') // ES5 iife var SingletonTester = (function () { function Singleton(args) { var args = args || {}; //設置name參數 this.name = 'SingletonTester'; } //實例容器 var instance; return { name: 'SingletonTester', getInstance: function (args) { if (instance === undefined) { instance = new Singleton(args); } return instance; } }; })(); var singletonTest = SingletonTester.getInstance({ pointX: 5 }); console.log(singletonTest.pointX); // 輸出 5 // 構造函數的屬性 function Universe() { if (typeof Universe.instance === 'object') { return Universe.instance; } this.start_time = 0; this.bang = "Big"; Universe.instance = this; } // 測試 var uni = new Universe(); var uni2 = new Universe(); console.log(uni === uni2); // true // 重寫構造函數 function Universe() { var instance = this; // 其它內容 this.start_time = 0; this.bang = "Big"; // 重寫構造函數 Universe = function () { return instance; }; } // 測試 var uni = new Universe(); var uni2 = new Universe(); uni.bang = "123"; console.log(uni === uni2); // true console.log(uni2.bang); // 123
工廠模式定義一個用於建立對象的接口,這個接口由子類決定實例化哪個類。該模式使一個類的實例化延遲到了子類。而子類能夠重寫接口方法以便建立的時候指定本身的對象類型。this
簡單說:假如咱們想在網頁面裏插入一些元素,而這些元素類型不固定,多是圖片、連接、文本,根據工廠模式的定義,在工廠模式下,工廠函數只需接受咱們要建立的元素的類型,其餘的工廠函數幫咱們處理。url
// 文本工廠 class Text { constructor(text) { this.text = text } insert(where) { const txt = document.createTextNode(this.text) where.appendChild(txt) } } // 連接工廠 class Link { constructor(url) { this.url = url } insert(where) { const link = document.createElement('a') link.href = this.url link.appendChild(document.createTextNode(this.url)) where.appendChild(link) } } // 圖片工廠 class Image { constructor(url) { this.url = url } insert(where) { const img = document.createElement('img') img.src = this.url where.appendChild(img) } } // DOM工廠 class DomFactory { constructor(type) { return new (this[type]()) } // 各流水線 link() { return Link } text() { return Text } image() { return Image } } // 建立工廠 const linkFactory = new DomFactory('link') const textFactory = new DomFactory('text') linkFactory.url = 'https://surmon.me' linkFactory.insert(document.body) textFactory.text = 'HI! I am surmon.' textFactory.insert(document.body)
裝飾者(decorator)模式可以在不改變對象自身的基礎上,在程序運行期間給對像動態的添加職責(方法或屬性)。與繼承相比,裝飾者是一種更輕便靈活的作法。設計
簡單說:能夠動態的給某個對象添加額外的職責,而不會影響從這個類中派生的其它對象。代理
ES7裝飾器 function isAnimal(target) { target.isAnimal = true return target } // 裝飾器 @isAnimal class Cat { // ... } console.log(Cat.isAnimal) // true 做用於類屬性的裝飾器: function readonly(target, name, descriptor) { discriptor.writable = false return discriptor } class Cat { @readonly say() { console.log("meow ~") } } var kitty = new Cat() kitty.say = function() { console.log("woof !") } kitty.say() // meow ~