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:欢迎你们关注个人公众号【前端下午茶】,一块儿加油吧~

另外能够加入「前端下午茶交流群」微信群,长按识别下面二维码便可加我好友,备注加群,我拉你入群~

相关文章
相关标签/搜索