潛水了一段時間,今天空閒時間覆盤下以前的知識點,聊聊 js 幾種常見的設計模式。javascript
掌握 JavaScript 中常見的一些設計模式,對咱們書寫規範性代碼,可維護性代碼有很大的幫助。前端
ps:最近在一些好友的鼓勵下,pubdreamcc 準備着手經營一個公衆號了,具體信息會在接下來的兩天時間內發佈,新手上路,歡迎大夥提供一些寶貴的建議,cc 在這裏先謝了~java
所謂單例模式即爲:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。git
這裏其實利用的是 js閉包
來實現這樣的功能。github
假如如今咱們有這樣的需求,設置一個管理員,不管建立屢次都只是設置一次。web
function SetManager(name) { this.manager = name; } SetManager.prototype.getName = function() { console.log(this.manager); }; var SingletonSetManager = (function() { var manager = null; return function(name) { if (!manager) { manager = new SetManager(name); } return manager; } })(); SingletonSetManager('a').getName(); // a SingletonSetManager('b').getName(); // a SingletonSetManager('c').getName(); // a
這種方法有一個缺點就是:若是咱們須要再次建立一個 HR,則須要將代碼再複製一遍,因此咱們能夠提取通用的單例。算法
function getSingleton(fn) { var instance = null; return function() { if (!instance) { instance = fn.apply(this, arguments); } return instance; } } // 設置管理員 var managerSingleton = getSingleton(function(name){ var manager = new SetManager(name); return manager; }) managerSingleton('a').getName(); // a managerSingleton('b').getName(); // a managerSingleton('c').getName(); // a // 設置 HR function SetHr(name) { this.hr = name; } SetHr.prototype.getName = function() { console.log(this.hr); }; var hrSingleton = getSingleton(function(name) { var hr = new SetHr(name); return hr; }); hrSingleton('aa').getName(); // aa hrSingleton('bb').getName(); // aa hrSingleton('cc').getName(); // aa
這樣咱們的代碼可通用性就會變得更好,省去了一些重複性的代碼。設計模式
所謂代理模式就是:咱們不方便直接訪問某個對象時,能夠爲對象建立一個佔位符(代理),以便控制對它的訪問,咱們實際上訪問的是代理對象。閉包
這裏咱們以一個過濾敏感字符來講明這種模式app
// 主體,發送消息 function sendMsg(msg) { console.log(msg); } // 代理,對消息進行過濾 function proxySendMsg(msg) { // 無消息則直接返回 if (typeof msg === 'undefined') { console.log(null); return; } // 有消息則進行過濾 msg = ('' + msg).replace(/泥\s*煤/g, ''); sendMsg(msg); } sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀 proxySendMsg('泥煤呀泥 煤'); // 呀 proxySendMsg(); // null
這樣操做的意圖很明顯,當沒有消息的時候,控制對主體對象的訪問,代理直接返回一個 null
,有消息,則會過濾掉敏感字符,實現虛擬代理。
策略模式就是內部封裝一些算法,它們之間能夠互相替換,可是它們不隨客戶端變化而變化。
策略模式咱們外部看不到算法的具體實現,咱們也只關心算法實現的結果,不關注過程。
這裏以一個商品促銷的例子來講明下:在聖誕節,某些商品須要八折出售,有些商品須要九折出售,到了元旦節,普通客戶滿100減30,vip客戶滿100減50。能夠看到商品出售的價格須要根據不一樣的條件來規定,分別採起不一樣的算法實現,因此咱們採用策略模式。
// 價格策略對象 class PriceStrategy { constructor() { // 內部算法對象 this.stragtegy = { // 100返30 return30(price) { return +price + parseInt( price / 100) * 30; }, // 100 返 50 return50(price) { return +price + parseInt(price/ 100) * 50; }, // 9 折 percent90(price) { return price * 100 * 90 / 10000 }, percent80(price) { return price * 100 * 80 / 10000 }, percent50(price) { return price * 100 * 50 / 10000 } } } // 策略算法調用接口 getPrice(algorithm, price) { return this.stragtegy[algorithm] && this.stragtegy[algorithm](price); } } let priceStrategy = new PriceStrategy(); let price = priceStrategy.getPrice('return50', 314.67); console.log(price);
這樣,咱們能夠採起不一樣的策略算法獲得商品的不一樣價格。
觀察者模式又稱爲 「發佈-訂閱模式」,經過定義一種依賴關係,當一個對象狀態發生改變時,訂閱者會獲得通知。
其實,咱們傳統的 DOM 事件綁定就是一種發佈-訂閱模式。
// 訂閱 document.body.addEventListener('click', function() { console.log('click1'); }, false); document.body.addEventListener('click', function() { console.log('click2'); }, false); // 發佈 document.body.click(); // click1 click2
裝飾者模式就是在不改變原對象基本功能的基礎上,經過增長功能使得本來對象知足用戶的更爲複雜的需求。
好比有這麼一個需求:
用戶點擊輸入框時,若是輸入框輸入的內容有限制,那麼在其後面顯示用戶輸入內容的限制格式的提示文案
---------------->>>>>>> 如今要改成:
多加一條需求,默認輸入框上邊顯示一行文案,當用戶點擊輸入框的時候,文案消失。
這裏是之前的代碼:
// 輸入框元素 let telInput = document.getElementById('tel_input'); // 輸入框提示文案 let telWarnText = document.getElementById('tel_warn_text'); // 點擊輸入框顯示輸入框輸入格式提示文案 input.onclick = function () { telWarnText.style.display = 'inline-block'; };
修改以後的代碼:
// 輸入框元素 let telInput = document.getElementById('tel_input'); // 輸入框輸入格式提示文案 let telWarnText = document.getElementById('tel_warn_text'); // 輸入框提示輸入文案 let telDemoText = document.getElementById('tel_demo_text'); // 點擊輸入框顯示輸入框輸入格式提示文案 input.onclick = function () { telWarnText.style.display = 'inline-block'; telDemoText.style.display = 'none'; };
可是緊接着悲劇就來了,修改了電話輸入框,還有姓名、地址輸入框等等;
裝飾已有的功能對象
原有的功能已經不知足用戶的需求了,此時須要作的是對原有的功能添加,設置新的屬性和方法來知足新的需求,可是有不影響原來已經有的部分。
let decorator = function (input, fn) { let getInput = document.getElementById(input); if(typeof getInput.onclick === 'function') { let oldClick = getInput.onclick; getInput.onclick = function() { // 原來的事件回調函數 oldClick(); // 新增的事件回調函數 fn(); } } else { getInput.onclick = fn; } // 其餘事件 }; // 電話輸入框功能裝飾 decorator('tel_input', function() { document.getElementById('tel_demo_text').sytle.display = 'none' }); // 姓名輸入框裝飾 decorator('name_input', function() { document.getElementById('name_demo_text').sytle.display = 'none' }); // 地址輸入框裝飾 decorator('address_input', function() { document.getElementById('address_demo_text').sytle.display = 'none' });
本編文章出自於個人 github
倉庫 ,歡迎喜歡的夥伴 star
,謝謝 。
倉庫地址 前端學習