• 【设计模式-前端】单例模式深刻理解和实现


    【设计模式-前端】单例模式深刻理解和实现

    引言

    内容速递:看了本文您能了解到的知识

    看了本文的收获

    1、什么是单例模式

    一句话介绍:保证类的实例只有一个

    1.1、简述前端的类

    说到类,JS在ES6提供了更接近面向对象语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

    注意:JavaScript本质上是基于对象的语言

    1.2、类是语法糖

    其实,ES6的Class可以看作只是Function的语法糖,它的绝大部分功能,ES5的Function都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

    2、单例在前端的应用场景

    单例模式在前端应用范围内有很多应用场景。

    2.1、Vuex

    很典型的单例模式的应用。Vuex的store就是一个单例模式。采用了单一状态树,一个对象即为唯一数据源

    可以简单看一下Vuex源码

    let Vue 
    ...
     
    export function install (_Vue) {
      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 = _Vue
    
      applyMixin(Vue)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可以看到Vuex在install的时候,判断是否有了唯一的state。反之,如果没有这一段逻辑,在Vue中使用的时候

    // 安装插件
    Vue.use(Vuex)
    
    ...(Vuex操作)
    
    // 再次安装
    Vue.use(Vuex)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    再次安装的时候,唯一数据源store被重新生成,中间的Vuex操作就没有了

    2.2、Redux

    Redux提供的全局的store,和Vuex一样,必须是一个单例模式,不然每个组件各玩各的,如何实现数据协调同步呢。

    2.3、全局组件

    在前端中有一些全局组件,例如:创建全局唯一的对话框实例、记录日志信息全局唯一实例、登录弹窗、购物车等等。

    这些组件都有一个特点,只有一个实例,状态唯一。

    2.4、import

    在ES6中,import 引入的模块是会自动执行的,但是有的时候我们可能会重复引入一个模块,但是这个时候不会执行多次,因为import就用到了SIngleton模式。

    2.5、更多

    还想看吗?可重点不是列举额,那这里也不多列举了,只能给你展示更多了!

    3、单例的实现

    3.1、思路

    思路很简单,那就是一个类只有一个实例

    3.2、实现

    那怎么做到一个类只有一个实例?

    去获取一个类的实例之前,去判断一下这个实例是否存在,如果存在直接把这个实例返回,那这样,不就是一个类只有一个实例了吗?

    一个正常的类

    每次new出来的实例都是不一样的

    class SingleItem {
      show() {
        console.log('我是一个单例')
      }
    }
    
    const s1 = new SingleItem()
    const s2 = new SingleItem()
    
    console.log(s1 === s2)
    // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    借助Class和static实现单例

    class SingleItem {
    
      static instance
    
      show() {
        console.log('我是一个单例')
      }
    
      static getInstance() {
        return SingleItem.instance || (SingleItem.instance = new SingleItem())
      }
    }
    
    const s1 = SingleItem.getInstance()
    const s2 = SingleItem.getInstance()
    
    console.log(s1 === s2)
    // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4、单例的优点与弊端

    4.1、优点

    • 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
    • 由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
    • 避免对共享资源的多重占用。

    4.2、弊端

    • 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
    • 单例类的职责过重,在一定程度上违背了单一职责原则
    • 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。

    5、单例实战

    5.1、实现一个Storage

    听说是一个面试题!

    问题描述:

    实现一个Storage,让该对象成为单例对象,可以基于localStorage实现。

    思路:

    1、获取实例

    2、实例唯一

    代码:

    class Storage {
    
      static instance
    
      static getInstance() {
        return Storage.instance || (Storage.instance = new Storage())
      }
    
      getItem(key) {
        return localStorage.getItem(key)
      }
    
      setItem(key, value) {
        localStorage.setItem(key, value)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试一下:

    const s1 = Storage.getInstance()
    const s2 = Storage.getInstance()
    
    s1.setItem('name', 'aaa')
    
    // 测试两个的实例是否为同一个
    s1.getItem('name') // aaa
    console.log(s1.getItem('name'))
    s2.getItem('name') // aaa
    console.log(s2.getItem('name'))
    
    console.log(s1 === s2)
    // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.2、实现一个全局的Modal框

    面试题+1

    问题描述:

    用单例模式写一个全局的Modal框

    思路:

    1、获取实例

    2、实例唯一

    这里也可以通过闭包来实现

    代码:

    DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>单例Modal框title>
    head>
    <body>
    
      <div id="openModal">打开Modal框div>
      <div id="closeModal">关闭Modal框div>
      
    body>
    
    <script>
      // 实现Modal框
      const Modal = (function() {
        let modal = null
        return function() {
          if (!modal) {
            modal = document.createElement('div')
            modal.innerHTML = '我是Modal框'
            modal.style.display = 'none'
            document.body.appendChild(modal)
          }
          return modal
        }
      })()
    
      document.getElementById('openModal').addEventListener('click', function() {
        const modal = new Modal()
        modal.style.display = 'block'
      })
    
      document.getElementById('closeModal').addEventListener('click', function() {
        const modal = new Modal()
        modal && (modal.style.display = 'none')
      })
    script>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    总结

    慢慢开发的年龄大了,就越想搞点新玩意,但是编程的思维却越来越需要有足够的长进。最近要做一个JS-SDK,单例的思想值得好好琢磨琢磨!

    博客说明与致谢

    文章所涉及的部分资料来自互联网整理,其中包含自己个人的总结和看法,分享的目的在于共建社区和巩固自己。

    引用的资料如有侵权,请联系本人删除!

    感谢万能的网络,W3C,菜鸟教程等!

    感谢勤劳的自己个人博客GitHub学习GitHub

    公众号【归子莫】,小程序【子莫说】

    如果你感觉对你有帮助的话,不妨给我点赞鼓励一下,好文记得收藏哟!关注我一起成长!

    幸好我在,感谢你来!

  • 相关阅读:
    flutter发布android
    Python从零基础入门到精通:一个月就够了
    论文解读(AutoSSL)《Automated Self-Supervised Learning for Graphs》
    树状数组及其拓展
    Python之正则表达式
    阿里P8MySQL,基础/索引/锁/日志/调优都不误,一锅深扒端给你
    中英文说明书丨CalBioreagents艾美捷重组Ku-p70/p80蛋白
    【Python】【技能树评测】技巧实例-说明改进和实践【04】访问限制
    【从0到1设计一个网关】性能优化---缓存
    机器学习库实战:DL4J与Weka在Java中的应用
  • 原文地址:https://blog.csdn.net/qq_45163122/article/details/126282555