前端設計模式(2)--單例模式

duffy發表於2019-02-13

設計模式清單

沒有連結的是還沒有寫的,計劃要寫的,歡迎閱讀交流~
前端設計模式(0)物件導向&&設計原則
前端設計模式(1)--工廠模式
前端設計模式(2)--單例模式
前端設計模式(3)--介面卡模式
前端設計模式(4)--裝飾器模式
前端設計模式(5)--代理模式
前端設計模式(6)--外觀模式&&觀察者模式
前端設計模式(7)--狀態和策略模式
前端設計模式(8)--原型模式
...

單例模式是什麼?

單例就是保證一個類只有一個例項,實現方法一般是先判斷例項存在與否,如果存在直接返回,如果不存在就建立了再返回,這就確保了一個類只有一個例項物件。在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)
複製程式碼

缺點:

  1. 客戶端 就是使用這個類的使用者必須知道這是一個單例的類,必須主動呼叫getInstance方法,使用麻煩
  2. 並不能真正阻止客戶端直接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);
複製程式碼
  1. 建立一個this=空物件
  2. 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);
複製程式碼

今天寫到這裡,喜歡的可以點贊,同時歡迎拋轉,有空再寫下總結吧~

相關文章