天天看點

javascript設計模式_JavaScript 設計模式 單例模式

本文原載于 SegmentFault 社群專欄:前端學習 作者: li uxuan

什麼是單例模式

單例模式(Singleton Pattern)確定一個類隻有一個執行個體,并提供一個通路它的全局通路點

單例模式是設計模式中較為簡單,好了解的一種模式,但是它的使用場景是很廣泛的,包括大名鼎鼎的 Vuex 也使用了單例模式。 本文會介紹單例模式的兩種實作方法: 類和閉包,同時也會對 Vuex 中的單例模式進行介紹。

實作方法

類 Class 是 ES6 新增的文法,在之前我們想要建立一個對象執行個體,是通過 new 構造函數的方式來實作的。 我們每次調用 new 的時候,都會生成一個新的執行個體對象,每個執行個體對象之間是完全獨立的。

function Car (name) {this.name = name;
}var car1 = new Car('benz');var car2 = new Car('audi');console.log(car1 === car2)      // false
           

那麼我們來思考一下,怎麼才能每次 new 都傳回同一個執行個體對象呢? 肯定是有一個變量将第一次 new 生成的執行個體對象儲存了下來,後面再執行 new 的時候,就直接傳回第一次生成的執行個體對象,這樣就實作了單例。 我們通過兩種方法來實踐一下: 類和閉包。

類實作

class SingletonCar {constructor () {this.name = 'benz';
    }static getInstance () {if (!SingletonCar.instance) {
            SingletonCar.instance = new SingletonCar();
        }return SingletonCar.instance;
    }
}let car1 = SingletonCar.getInstance();let car2 = SingletonCar.getInstance();console.log(car1 === car2)      // true
           

類相當于執行個體的原型,所有在類中定義的方法,都會被執行個體繼承。 如果在一個方法前,加上 static 關鍵字,就表示該方法不會被執行個體繼承,而是直接通過類來調用,這就稱為“靜态方法”。 靜态方法可以直接在父類上調用 SingletonCar.getInstance(),而不是在執行個體對象上調用。 如果在執行個體上調用靜态方法,會抛出一個錯誤,表示不存在該方法。 用類來實作單例模式,隻要記住這個 getInstance() 靜态方法就可以了。

閉包實作

var SingletonCar = (function () {var instance;var SingletonCarTemp = function () {this.name = 'benz';
    };return function () {if (!instance) {
            instance = new SingletonCarTemp();
        }return instance;
    }
})();var car1 = new SingletonCar();var car2 = new SingletonCar();console.log(car1 === car2)      // true
           

借助閉包,在記憶體中保留了 instance 變量,不會被垃圾回收,用來儲存唯一的執行個體,多次調用 new 的時候,隻傳回第一次建立的執行個體。

Vuex 中的單例模式

Vuex 是 Vue 的狀态管理類庫,類似于 Redux 之于 React,其實他們的理念都是來自于 Flux 架構,用一個全局的 Store 存儲應用所有的狀态,然後提供一些 API 供使用者去讀取和修改。 一看到全局唯一的 Store,就可以想到是單例模式了。

如何引用 Vuex

import Vue from 'vue'import Vuex form 'vuex'import store from './store'
Vue.use(Vuex)new Vue({
  el: '#app',
  store
})
           

Vuex 是一個插件,通過調用 Vue.use() 方法可以安裝這個插件,具體 Vue 插件的寫法可以參考官方文檔 Vuex 在内部實作了一個 install 方法,該方法會在 Vue.use() 時被調用,Vue 會被作為參數傳入 install 方法中,進而把 Store 注入到 Vue 中。 但是如果你試過在開發環境多次調用 Vue.use(Vuex) 的話,就會知道是浏覽器是會報錯的,接下來我們看一下 Vuex 的内部實作。

Vuex 如何保證唯一 Store

上 Vuex 部分源碼:

let Vue
...
export function install (_Vue) {// 是否已經執行過了 Vue.use(Vuex),如果在非生産環境多次執行,則提示錯誤if (Vue && _Vue === Vue) {if (process.env.NODE_ENV !== 'production') {console.error('[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }return
  }// 如果是第一次執行 Vue.use(Vuex),則把傳入的 _Vue 指派給定義的變量 Vue
  Vue = _Vue// Vuex 初始化邏輯
  applyMixin(Vue)
}
           

完整源碼路徑在 vuex/src/store.js 我們可以看到,在 Vuex 内部,先定義了一個變量 Vue,注意這裡不是真正的 Vue,隻是一個變量,也叫 Vue。 在 Vue.use(Vuex) 的時候,會調用 install 方法,真正的 Vue 會被當做參數傳入,如果多次執行 Vue.use(Vuex),也隻會生效一次,也就是隻會執行一次 applyMixin(Vue),是以隻會有一份唯一的 Store,這就是 Vuex 中單例模式的實作。

練習

實作一個全局唯一的 Loading 遮罩。

思路

我們在業務開發的過程中,有很多需求都會有 Loading 狀态,這時候直接掏出單例模式,記住上面的 getInstance 靜态方法或者閉包 instance 變量,三下五除二即可實作。

完整代碼

html><html ><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>單例模式全局Loadingtitle><style>.loading {width: 100vw;height: 100vh;line-height: 100vh;position: absolute;top: 0;left: 0;background-color: #000;opacity: .7;color: #fff;text-align: center;
    }style>head><body><button id="startLoading">點選加載button><script>const Loading = (function () {let instancereturn function () {if (!instance) {
          instance = document.createElement('div')
          instance.innerHTML = '加載中...'
          instance.id = 'loading'
          instance.className = 'loading'document.body.appendChild(instance)
        }return instance
      }
    })()document.getElementById('startLoading').addEventListener('click', () => {const loading = new Loading()
    })script>body>html>
           

總結

單例模式,就是確定一個類隻有一個執行個體,如果采用 class 來實作,記住 getInstance 靜态方法,如果采用閉包來實作,記住 instance 變量。 當我們在業務開發中,遇到類似于 Vuex 這種需要全局唯一狀态的時候,就是單例模式登場的時候。 - END -

javascript設計模式_JavaScript 設計模式 單例模式

繼續閱讀