紙上得來終覺淺,學習設計模式,看了不少書,可是始終仍是以爲不如直接看例子來的更加客觀具體,下面主要記錄了js中的幾個常見的設計模式舉例,供本身之後複習的時候能夠直接經過例子更快更好的理解設計模式。node
保證一個類僅有一個實例,並提供一個全局訪問入口ajax
var getSingleton = function(fn){ var result; return function(){ return result || (result = fn.apply(this, arguments)); } } var createLoginLayer = function(){ var div; return function(){ if(!div){ div = document.createElement('div'); div.innerText = '這是彈窗'; div.style.display = 'none'; document.body.appendChild(div); } return div; } }); var singletonCreateLoginLayer = getSingleton(createLoginLayer); document.getElementById('loginBtn').onclick = function(){ var layer = singletonCreateLoginLayer(); layer.style.display = 'block'; }
定義一系列算法,並使之能夠相互替換。目的就是使算法的使用和定義分離出來。算法
var Strategy = { S: function(salary){ return salary * 4; }, A: function(salary){ return salary * 3; }, B: function(salary){ return salary * 2; } }; var getBouns = function(strategy, salary){ return Strategy[strategy](salary); } getBouns('B', 1000); // 2000
表單校驗設計模式
var strategies = { isNonEmpty: function(value, errorMsg){ .... }, minLength: function(value, length, errorMsg){ .... }, isMobile: function(value, errorMsg){ ... } }; var Validator = function(){ this.cache = []; }; Validator.prototype.add = function(dom, rule, errorMsg){ var ary = [rule]; this.cache.push(function(){ var strategy = ary.shift(); ary.unshift(dom.value); ary.push(errorMsg); return strategies[strategy].apply(dom, ary); }); } Validator.prototype.start = function(){ for(var i=0, validatorFunc; validatorFunc = this.cache[i++]){ var msg = validatorFunc(); if(msg){ return msg; } } } var validatorFunc = function(){ var validator = new Validator(); // 建立一個對象 validator.add(...); var errorMsg = validator.start(); // 獲取校驗結果 } var registerForm = document.getElementById('form'); registerForm.onsubmit = function(){ var errorMsg = validatorFunc(); if(errorMsg){ return false; } }
爲一個對象提供一個代用品或佔位符,以便控制對它的訪問。當客戶不方便直接訪問一個對象或者不知足須要的時候提供一個替身對象來控制對這個對象的訪問,客戶實際上訪問的是替身對象。性能優化
單一職責原則指的是,就一個類(一般也包括對象和函數等)而言,應該僅有一個引發它變 化的緣由。若是一個對象 了多 職責,就意味着這個對象將變得 大,引發它變化的緣由可 能會有多個。面向對象設計 將行爲分 到細 度的對象之中,若是一個對象 的職責過多, 等於把這些職責耦合到了一塊兒,這種耦合會 致 和低內聚的設計。當變化發生時,設計可能 會 到意外的 。app
圖片懶加載dom
const myImg = ( const node = documnet.createElement('img') document.body.appendChild(node) return { setSrc(src) { node.src= src } } )() const proxy = ( const img = new Image() img.onload = () => { myImg.setSrc(this.src) } return { setImg(src) { img.src = src myImg.setSrc('loading.gif') } } )()
發佈訂閱模式又叫觀察者模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知。在JavaScript開發中,咱們通常用事件模型來替代傳統的發佈訂閱模式。函數
const Event = ( function() { var eventList = {} var addEventListen var trigger var remove addEventListen = function(eventName, fn) { eventList[eventName] = eventList[eventName] || [] eventList[eventName].push(fn) } trigger = function() { var key = Array.prototype.shift.call(arguments) var fns = eventList[key] if (!fns || !fns.length) { return } fns.forEach((fn, index) => { fn.apply(this, arguments) }) } remove = function(eventName, fn) { var fns = eventList[eventName] if (!fns || !fns.length) { return false } if (!fn) { fns.length = 0 } else { fns.forEach((_fn, index) => { if(fn === _fn) { fns.splice(index, 1) } }) } } return { addEventListen, trigger, remove } } )() var testFn = () => { console.log('you have click a cancel btn') } Event.addEventListen('click', () => { console.log('you have click a button') }) Event.addEventListen('click', () => { console.log('you have click button2') }) Event.addEventListen('click', () => { console.log('you have click button3') }) Event.addEventListen('click', testFn) Event.remove('click', testFn) Event.trigger('click')
享元模式是爲性能優化而生的,在一個存在大量類似對象的系統中,享元模式能夠很好地解決大量對象帶來的性能問題性能
// uploadType做爲內部狀態,再抽離外部狀態 var Upload = function(uploadType){ this.uploadType = uploadType; }; // 定義刪除文件的方法 Upload.prototype.delFile = function(id){ uploadManager.setExternalState(id, this); // 設置外部狀態 if(this.fileSize < 3000){ return this.dom.parentNode.removeChild(this.dom); } if(window.confirm('肯定要刪除文件嗎?'+ file.fileName)){ return this.dom.parentNode.removeChild(this.dom); } }; // 工廠進行對象實例化 var UploadFactory = (function(){ var createFlyWeightObjs = {}; return { create: function(uploadType){ if(createFlyWeightObjs[uploadType]){ return createFlyWeightObjs[uploadType]; } return createFlyWeightObjs[uploadType] = new Upload(uploadType); } } })(); // 管理器封裝外部狀態 var uploadManager = (function(){ var uploadDataBase = {}; // 存儲外部狀態 return { add: function(id, uploadType, fileName, fileSize){ var flyWeightObj = UploadFactory.create(uploadType); var dom = document.createElement('div'); dom.innerHTML = '...'; document.body.appendChild(dom); uploadDataBase[id] = { // 添加外部狀態 fileName: fileName, fileSize: fileSize, dom: dom }; return flyWeightObj; }, setExternalState: function(id, flyWeightObj){ // 設置外部狀態 var uploadData = uploadDataBase[id]; for(var i in uploadData){ flyWeightObj[i] = uploadData[i]; } } } })(); var id = 0; window.startUpload = function(uploadType, files){ for(var i = 0, file; file = files[i++];){ var uploadObj = uploadManager.add(++id, uploadType, file.fileName, file.fileSize); } }; startUpload('plugin', [ { fileName: '1.txt', fileSize: 1000 }, ... ]);
對象池維護一個裝載空閒對象的池子,若是須要對象的時候,不是直接new,而是轉從對象池裏獲取。若是對象池沒有空閒對象,則建立一個新的對象,當獲取出的對象完成它的職責以後,再進入池子等待被下次獲取。學習
var objectPoolFactory = function(createObjFn){ var objectPool = []; return { create: function(){ var obj = objectPool.length === 0 ? createObjFn.apply(this, arguments) : objectPool.shift(); return obj; }, recover: function(obj){ objectPool.push(obj); } }; }; // 如今利用`ObjectPoolFactory`來建立一個裝載一些`iframe`的對象池 var iframeFactory = objectPoolFactory(function(){ var iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.onload = function(){ iframe.onload = null; iframeFactory.recover(iframe); } return iframe; }); var iframe1 = iframeFactory.create(); iframe1.src = 'http://baidu.com'; var iframe2 = iframeFactory.create(); iframe2.src = 'http://qq.com'; setTimeout(function(){ var iframe3 = iframeFactory.create(); iframe3.src = 'http://163.com'; }, 3000);
用一箇中介者對象來封裝一系列的對象交互。中介者使各個對象之間不會相互引用。從而使其達到鬆散耦合的目的。
與觀察者模式對比來看,中介者模式是觀察者模式中的共享被觀察者對象。在這個系統中的對象之間直接的發佈/訂閱關係被犧牲掉了,取而代之的是維護一個通訊的中心節點。
寫程序是爲了快速完成項目交付生產,而不是堆砌模式和過渡設計。關鍵就在於如何去衡量對象之間的耦合程度。若是對象之間的複雜耦合確實致使調用和維護出現了困難,並且這些耦合度隨項目的變化呈指數增加曲線,那就能夠考慮用中介者模式來重構代碼。
function Player(name, teamColor){ this.name = name; // 角色名字 this.teamColor = teamColor; // 隊伍顏色 this.state = 'alive'; // 玩家生存狀態 } Player.prototype.win = function(){ console.log('winner:' + this.name); }; Player.prototype.lose = function(){ console.log('loser:' + this.name); }; Player.prototype.die = function(){ this.state = 'dead'; playerDirector.ReceiveMessage('playerDead', this); // 給中介者發送消息,玩家死亡 }; Player.prototype.remove = function(){ playerDirector.ReceiveMessage('removePlayer', this); // 給中介者發送消息,移除一個玩家 }; Player.prototype.changeTeam = function(){ playerDirector.ReceiveMessage('changeTeam', this); // 給中介者發送消息,玩家換隊 }; var playerFactory = function(name, teamColor){ var newPlayer = new Player(name, teamColor); playerDirector.ReceiveMessage('addPlayer', newPlayer); // 給中介者發送消息,新增玩家 return newPlayer; }; // 實現playerDirector對象 var playDirector = (function(){ var players = {}; // 保存全部玩家 var operations = {}; // 中介者能夠執行的操做 // 新增一個玩家 operations.add = function(player){ var teamColor = player.teamColor; players[teamColor] = players[teamColor] || []; players[teamColor].push(player); }; // 移除一個玩家 operations.removePlayer = function(player){ var teamColor = player.teamColor; var teamPlayers = players[teamColor] || []; for(var i=teamPlayers.length - 1; i >= 0 ;i --){ if(teamPlayers[i] === player){ teamPlayers.splice(i, 1); } } }; // 玩家換隊 operations.changeTeam = function(player, newTeamColor){ operations.removePlayer(player); // 從原隊伍中刪除 player.teamColor = newTeamColor; // 換顏色 operations.addPlayer(player); // 新增玩家到新的隊伍 } operations.playerDead = function(player){ var teamColor = player.teamColor; var teamPlayer = players[teamColor]; var all_dead = true; // 遍歷隊友列表 for(var i=0, player; player = teamPlayer[i++];){ if(player.state !== 'dead'){ all_dead = false; break; } } // 若是隊友所有死亡 if(all_dead === true){ this.lose(); // 通知全部隊友玩家遊戲失敗 for(var i=0, player; player = teamPlayer[i++];){ player.lose(); } // 通知全部敵人遊戲勝利 for(var color in players){ if(color !== teamColor){ var teamPlayers = players[color]; for(var i=0, player; player = teamPlayers[i++];){ player.win(); } } } } } var ReceiveMessage = function(){ var message = Array.prototype.shift.call(arguments); operations[message].apply(this, arguments); }; return { ReciveMessage: ReceiveMessage } })(); // 建立8個玩家對象 var player1 = playerFactory('a', 'red'); var player2 = playerFactory('b', 'red'); var player3 = playerFactory('c', 'red'); var player4 = playerFactory('d', 'red'); var player5 = playerFactory('e', 'blue'); var player6 = playerFactory('f', 'blue'); var player7 = playerFactory('g', 'blue'); var player8 = playerFactory('h', 'blue');
給對象動態地增長職責的方式稱爲裝飾者模式。
裝飾者模式可以在不改變對象自身的基礎上,在程序運行期間給對象動態地添加職責。跟繼承相比,裝飾者是一種更輕便靈活的作法,這是一種「即用即付」的方式。
函數經過Function.prototype.before或者Function.prototype.after被裝飾以後,返回的其實是一個新的函數,若是在原函數上保存了一些屬性,那麼這些屬性會丟失。
這種裝飾方式也疊加了函數的做用域,若是裝飾的鏈條過長,性能上也會受到一些影響。
Function.prototype.before = function(beforeFn){ var _self = this; return function(){ if(beforefn.apply(this, arguments) === false){ return; }; return _self.apply(this, arguments); } }; var validata = function(){ if(username.value === ''){ alert('不能爲空'); return false; } if(password.value === ''){ alert('不能爲空'); return false; } }; var formSubmit = function(){ var param = { username: username.value, password: password.value }; ajax('....'); }; formSubmit = formSubimt.before(validata); submitBtn.onclick = function(){ formSubmit(); };
狀態模式的關鍵是把事物的每種狀態都封裝成單獨的類,跟此種狀態有關的行爲都被封裝在這個類的內部,只須要在上下文中,把某個請求委託給當前的狀態對象便可,該狀態對象會福州渲染它自身的行爲。
// Light 類 var Light = function(){ this.offLightState = new OffLightState(this); this.weekLightState = new WeekLightState(this); this.strongLightState = new StrongLightState(this); this.button = null; }; Light.prototype.init = function(){ var button = document.createElement('button'); var self = this; this.button = document.body.appendChild(button); this.button.innerHTML = '開關'; this.currState = this.offLightState; this.button.onclick = function(){ self.currState.buttonWasPressed(); }; }; // offLightState var OffLightState = function(light){ this.light = light; }; OffLightState.prototype.buttonWasPressed = function(){ console.log('弱光') // offLightState 對應的行爲 this.light.setState(this.light.weekLightState); // 切換狀態到 weekLightState };
文中代碼主要來自曾探老師的《JavaScript設計模式與開發實踐》,書中的內容關於設計模式寫的更加詳實細緻,若是想學習設計模式推薦這本書入門哈!