JS 單例模式

1. 單例模式

單例模式 (Singleton) 的實如今於保證一個特定類只有一個實例,第二次使用同一個類建立新對象的時候,應該獲得與第一次建立對象徹底相同的對象。
當建立一個新對象時,實際上沒有其餘對象與其相似,由於新對象已是單例了 {a:1} === {a:1} // falsehtml

可是如何在對構造函數使用 new 操做符建立多個對象的時候僅獲取一個單例對象呢。前端

2. 靜態屬性中的實例

在構造函數的靜態屬性中緩存該實例,缺點在於 instance 屬性是公開可訪問的屬性,在外部代碼中可能會修改該屬性。segmentfault

function Universe() {
    if (typeof Universe.instance === 'object') {        // 判斷是否已經有單例了
        return Universe.instance
    }
    Universe.instance = this
    return this
}
var uni1 = new Universe()
var uni2 = new Universe()
uni1 === uni2            // true

3. 閉包中的實例

能夠把實例封裝在閉包中,這樣能夠保證該實例的私有性而且保證該實例不會在構造函數以外被修改,代價是帶來了額外的閉包開銷。設計模式

function Universe() {
    var instance = this
    Universe = function() {    // 重寫構造函數
        return instance
    }
}
var uni1 = new Universe()
var uni2 = new Universe()
uni1 === uni2         // true

當第一次調用構造函數時,它正常返回 this ,而後在之後調用時,它將會執行重寫構造函數,這個構造函數經過閉包訪問了私有 instance 變量,而且簡單的返回了該 instance緩存

4. 惰性單例

有時候對於單例對象須要延遲建立,因此在單例中還存在一種延遲建立的形式,也有人稱之爲惰性建立微信

const LazySingle = (function() {
  let _instance              // 單例的實例引用
 
  function Single() {        // 單例構造函數
    const desc = '單例'        // 私有屬性和方法
    return {                   // 暴露出來的對象
      publicMethod: function() {console.log(desc)},
      publickProperty: '1.0'
    }
  }
  
  return function() {
    return _instance || (_instance = Single())
  }
})()

console.log(LazySingle()===lazySingle())        // true
console.log(LazySingle().publickProperty)       // 1.0

5. 改進

以前在構造函數中重寫自身會丟失全部在初始定義和重定義之間添加到其中的屬性。在這種狀況下,任何添加到 Universe() 的原型中的對象都不會存在指向由原始實現所建立實例的活動連接:閉包

function Universe() {
    var instance = this
    Universe = function() {
        return instance
    }
}
Universe.prototype.nothing = true
var uni1 = new Universe()
Universe.prototype.enthing = true
var uni2 = new Universe()
console.log(uni1 === uni2) // true

uni1.nothing // true
uni2.nothing // true
uni1.enthing // undefined
uni2.enthing // undefined
uni1.constructor.name // "Universe"
uni1.constructor === Universe // false

之因此 uni1.constructor 再也不與 Universe() 相同,是由於uni1.constructor仍然指向原始的構造函數,而不是重定義以後的那個構造函數。
能夠經過一些調整實現原型和構造函數指針按照預期的那樣運行:函數

function Universe() {
    var instance
    Universe = function Universe() {
        return instance
    }
    Universe.prototype = this // 保留原型屬性
    instance = new Universe()
    instance.constructor = Universe // 重置構造函數指針
    instance.start_time = 0 // 一些屬性
    instance.big = 'yeah'
    return instance
}
Universe.prototype.nothing = true
var uni1 = new Universe()
Universe.prototype.enthing = true
var uni2 = new Universe()
console.log(uni1 === uni2) // true

uni1.nothing & uni2.nothing & uni1.enthing & uni2.enthing // true
uni1.constructor.name // "Universe"
uni1.constructor === Universe // true
uni1.big    // "yeah"
uni2.big    // "yeah"

本文是系列文章,能夠相互參考印證,共同進步~學習

  1. JS 抽象工廠模式
  2. JS 工廠模式
  3. JS 建造者模式
  4. JS 原型模式
  5. JS 單例模式
  6. JS 回調模式
  7. JS 外觀模式
  8. JS 適配器模式
  9. JS 利用高階函數實現函數緩存(備忘模式)
  10. JS 狀態模式
  11. JS 橋接模式
  12. JS 觀察者模式

網上的帖子大多深淺不一,甚至有些先後矛盾,在下的文章都是學習過程當中的總結,若是發現錯誤,歡迎留言指出~this

參考:
《JavaScript模式》 P143
《Javascript 設計模式》 - 張榮銘
設計模式之單例模式

PS:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~

另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~

相關文章
相關標籤/搜索