JS 单例模式

1. 单例模式

单例模式 (Singleton) 的实现在于保证一个特定类只有一个实例,第二次使用同一个类创建新对象的时候,应该得到与第一次创建对象完全相同的对象。 当创建一个新对象时,实际上没有其他对象与其类似,因为新对象已经是单例了 {a:1} === {a:1} // false

但是如何在对构造函数使用 new 操作符创建多个对象的时候仅获取一个单例对象呢。

2. 静态属性中的实例

在构造函数的静态属性中缓存该实例,缺点在于 instance 属性是公开可访问的属性,在外部代码中可能会修改该属性。

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 观察者模式

网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

参考: 《JavaScript模式》 P143 《Javascript 设计模式》 - 张荣铭 设计模式之单例模式

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一个骚气的文章目录自动生成器了解一下

    这个插件根据选定的目录内容中的 h1, h2, h3, h4, h5, h6 标签来自动生成目录插入到选定的目录容器中,并且提供一个漂亮的样式效果

    前端下午茶
  • GraphQL 从入门到实践

    本文首先介绍了 GraphQL,再通过 MongoDB + graphql + graph-pack 的组合实战应用 GraphQL,详细阐述如何使用 Grap...

    前端下午茶
  • 手摸手 Webpack 多入口配置实践

    再一次,在网上找的不少文章都不合我的需求,很多文章都是只简单介绍了生产环境下配置,没有介绍开发环境下的配置,有的也没有将多入口结合 vue-router、vue...

    前端下午茶
  • 用分布式技术轻松化解数据库容量和性能瓶颈

    我们的目标是您的业务仅需要 2 个数据库就够了,一个用来部署正式业务,不增加存储成本基础上,能涵盖 OLTP&OLAP 场景,且可以覆盖多种数据类型;另一个,一...

    腾讯云数据库团队
  • ECCV 2020 的对抗相关论文(对抗生成、对抗攻击)

    本文汇总了ECCV 2020上部分对抗相关论文,后续公众号会随缘对一些paper做解读。感兴趣的同学,可先自行根据标题,搜索对应链接(有些paper可能未公布)...

    公众号机器学习与生成对抗网络
  • 机器学习在客户管理场景中的应用

    版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

    Jerry Wang
  • TCGA、ICGC、GTEx 数据库都是啥?

    我们在进行数据库介绍,尤其是肿瘤相关数据库的时候,经常会提到说这个使用了 TCGA/GTEx 数据库的数据,那么这两个数据库到底是什么呢?为什么会有用这两个数据...

    医学数据库百科
  • PAT--L3-001. 凑零钱

    题目链接:https://www.patest.cn/contests/gplt/L3-001

    指点
  • 消息队列高可用

    常见的消息队列有ActiveMQ,RabbitMQ,RocketMQ,kafka,前两个属于集群模式部署来提供高可用,后两个可以部署分布式模式提供HA。

    黑白格
  • JAVA内存模型和Happens-Before规则

    上一篇文章王子给大家介绍了并发编程中比较关心的三个核心问题,可见性、有序性和原子性。

    HUC思梦

扫码关注云+社区

领取腾讯云代金券