前端設計模式(2)--單例模式
單例就是保證一個類只有一個例項,實現方法一般是先判斷例項存在與否,如果存在直接返回,如果不存在就建立了再返回,這就確保了一個類只有一個例項物件。在JavaScript裡,單例作為一個名稱空間提供者,從全域性名稱空間裡提供一個唯一的訪問點來訪問該物件。
程式碼
單例模式的簡單實現
es5
function Window(name) { this.name = name; } Window.prototype.getName = function () { return this.name; } //這是類上的方法,只可以通過類來訪問,而不能通過例項來訪問 Window.getInstance = (function () { let instance; return function (name) { if (!instance) { instance = new Window(name); } return instance; } })(); let w1 = Window.getInstance(); let w2 = Window.getInstance(); console.log(w1 === w2) // --> true 複製程式碼
es6
class Window { constructor(name) { this.name = name; } static getInstance() { if (!this.instance) { this.instance = new Window(); } return this.instance; } } let w1 = Window.getInstance(); let w2 = Window.getInstance(); console.log(w1 === w2) 複製程式碼
缺點:
- 客戶端 就是使用這個類的使用者必須知道這是一個單例的類,必須主動呼叫getInstance方法,使用麻煩
- 並不能真正阻止客戶端直接new Window
let w3 = new Window(); let w4 = new Window(); 複製程式碼
優化過程
透明單例
//透明單例 let Window = (function () { let window; let Window = function (name) { if (window) { return window; } else { this.name = name; return (window = this); } } return Window; })(); let w1 = new Window(); let w2 = new Window(); console.log(w1 === w2); 複製程式碼
- 建立一個this=空物件
- new 關鍵字,如果返回的是一個物件,那麼返回物件
但是有個問題:違反了單一職責原則,不夠清晰,所以需要改進一下
//把類的例項的建立邏輯和單例邏輯分開 function Window(name) { this.name = name; } Window.prototype.getName = function () { console.log(this.name); } let CreateWindow = (function () { let instance; return function (name) { if (!instance) { instance = new Window(name); } return instance; } })(); let w1 = new CreateWindow('zfpx1'); let w2 = new CreateWindow('zfpx2'); console.log(w1 === w2); 複製程式碼
現在是清晰了,但是還有一個小問題,Window給寫死了,作為一個優秀的程式設計師,肯定要靈活,來來再改進一下。
function Window(name) { this.name = name; } Window.prototype.getName = function () { console.log(this.name); } let CreateWindow = (function () { let instance; return function (name) { if (!instance) { instance = new Window(name); } return instance; } })(); let w1 = new CreateWindow('zfpx1'); let w2 = new CreateWindow('zfpx2'); console.log(w1 === w2); 複製程式碼
名稱空間
1.變數名衝突
2.複雜層次物件的可讀性要求
其實我們之前用的jquery並沒有把變數都宣告在 window上,而是都掛在$物件 jQuery,來,栗子兩枚:
$.get(); $.post(); $.ajax(); let $ = { ajax(){}, get(){}, post(){} } let $ = {}; $.define = function (namespace, fn) { let namespaces = namespace.split('.'); let fnName = namespaces.pop(); let current = $; for (let i = 0; i < namespaces.length; i++) { let namespace = namespaces[i];//dom if (!current[namespace]) { current[namespace] = {};//{dom:{}} } current = current[namespace]; } current[fnName] = fn; } $.define('dom.class.addClass', function () { console.log('dom.class.addClass'); }); $.define('dom.attr', function () { console.log('dom.attr'); }); $.define('string.trim', function () { console.log('string.trim'); }); console.log($) // define、dom、string都掛載上了了 $.dom.class.addClass('title'); // -> dom.class.addClass $.dom.attr('src'); // -> dom.attr $.string.trim(' abc '); // -> string.trim 複製程式碼
單例模式的應用場景
jQuery
只會有一個jQuery例項
if(window.jQuery!=null){ return window.jQuery; }else{ //init~~~~~~~ } 複製程式碼
模態視窗
一般網站都會有使用者系統,例如我們去淘寶買東西,第一時間需要你先登入
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <button id="show-btn">顯示登入框</button> <button id="hide-btn">隱藏登入框</button> <script> class Login { constructor() { this.element = document.createElement('div'); this.element.innerHTML = ( ` 使用者名稱 <input name="username"/> 密碼 <input name="password"/> <input type="submit" value="登入"/> ` ); this.element.style.cssText = `width:100px;height:100px;position:absolute;left:50%;top:50%;margin-top:-50px;margin-left:-50px;display:none`; document.body.appendChild(this.element); } show() { this.element.style.display = 'block'; } hide() { this.element.style.display = 'none'; } static getInstance() { if (!this.instance) { this.instance = new Login(); } return this.instance; } } document.getElementById('show-btn').addEventListener('click', function () { Login.getInstance().show(); }); document.getElementById('hide-btn').addEventListener('click', function () { Login.getInstance().hide(); }); </script> </body> </html> 複製程式碼
store
redux 整 個應用只有一個倉庫,整 個倉庫只有一個狀態state
//redux 整 個應用只有一個倉庫,整 個倉庫只有一個狀態state function createStore(reducer) { let state; let listeners = []; function subscribe(listener) { listeners.push(listener); } function getState() { return state; } function dispatch(action) { state = reducer(state, action); } return { getState, dispatch, subscribe } } let reducer = function () { } let store = createStore(reducer); 複製程式碼
今天寫到這裡,喜歡的可以點贊,同時歡迎拋轉,有空再寫下總結吧~