JS设计模式(二)——单例模式

概念

单例模式(Singleton Pattern)是一种设计模式,它确保一个类在系统中只有一个实例。它提供了一个全局访问点,让所有对象都可以获取该类的实例,从而避免创建多个实例,避免了重复创建和资源浪费的问题。

使用场景

比如,在一个应用程序中,有一个按钮,当点击该按钮时,弹出一个提示框。如果每次点击按钮时,都创建一个新的提示框实例,那么就会导致系统资源的浪费,因为这些实例对象很可能在使用完毕后立即被垃圾回收。使用单例模式后,每次点击按钮时,都会使用相同的提示框实例,这样就可以避免重复创建实例对象,减少系统资源的浪费。

1. 实现单例模式

在JavaScript中我们可以使用多种方式来实现单例模式,比如闭包,函数等,下面我们就来看看几种常见的手法是如何实现单例模式的。

1.1 函数

想要用函数实现单例我们首先得搞清楚它的一个逻辑:我们想创建实例对象并且调用它时,它可以知道我们是否创建了这个类(构造函数)的实例对象,如果没有创建这个类的实例对象就创建一个,如果有的话就继续用原来创建的实例对象,根据这个逻辑下面我们来看一下代码如何实现的:

js 复制代码
class SingleDog {
  constructor() {}
  show() {
    console.log('我是个单例对象')
  }

  static getInstance() {
    if (!SingleDog.instance) {
      SingleDog.instance = new SingleDog()
    }
    return SingleDog.instance
  }
}

const s1 = SingleDog.getInstance()
const s2 = SingleDog.getInstance()
console.log(s1 === s2);

// 输出:
// true

在上面的代码中我们是用es6中新增的语法类(class)来实现的,这个语法其实就是js中的构造函数里面的constructor(){}中的内容就是构造函数中的内容,class中的方法前面如果不加static那么实例对象就可以访问,如果添加了static那么只有这个类本身可以去访问,实例对象访问不到。

根据上面实现单例模式的逻辑,我们必须得在这个类中添加一个函数并且只有它本身能够调用,所以我们在这个方法面前加上static,在创建实例对象的时候,我们直接调用SingleDog中的getInstance这个方法即可,如果类中有instance这个属性那么就直接返回这个属性中所存储的实例对象即可,如果没有,那就往这个类中添加这个属性,并且将其的值赋为一个实例对象。

1.2 闭包

根据前面用函数来设计单例模式,我们可以发现其中一个很关键的点,就是得知道我们是不是用了对象来创建了实例对象,我们必须把这个实例对象给保存下来然后用来判断,而闭包恰好拥有保存变量这个特点,下面我们来看看代码实现:

js 复制代码
class SingleDog {
  
  constructor() {}
  show() {
    console.log('我是个单例对象')
  }

}

SingleDog.getInstance = (function () {
  let instance = null
  return function() {
    if (!instance) {
      instance = new SingleDog()
    }
    return instance
  }
})()
     
const s1 = SingleDog.getInstance()
const s2 = SingleDog.getInstance()
console.log(s1 === s2);

// 输出:
// true

这一次我们在类的外面定义了一个函数,并且把它立即执行,在这个立即函数中我们定义了一个变量instance用来判断这个类的实例对象是否已经被创建,然后返回一个函数。由于返回的函数中需要instance所以这个instance就被放入了闭包中可以用来判断,逻辑还是和上面在类上面添加静态方法一样。所以每次调用的时候就相当于是调用了这个立即执行函数所返回的那个函数,可以用来判断是否生成了实例对象。

2. 实战使用

在我们了解了这个单例模式之后,下面我们来看看在实战中如何使用这个单例模式,我们就拿开头所讲的那个场景来实现一下,下面是html代码部分:

js 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #model {
      width: 200px;
      height: 200px;
      line-height: 200px;
      text-align: center;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      border: 1px solid #000;
    }
  </style>
  </style>
</head>
<body>
  <button id="open">打开弹窗</button>
  <button id="close">关闭弹窗</button>
  <script>
    // 下面是失败代码
    // const model = function (flag) {
    //   let div = document.createElement('div')
    //   div.innerHTML = '我是一个全局弹框'
    //   div.id = 'model'
    //   div.style.display = flag// 不占位子会脱离文档流
    //   document.body.appendChild(div)
    // }
      
    // 下面是成功代码
    const Model = (function () {
      let model = null
      return function (flag) {
        if (!model) {
          model = document.createElement('div')
          model.innerHTML = '我是一个全局弹框'
          model.id = 'model'
          document.body.appendChild(model)
        }
        model.style.display = flag
        return model
      }
    })()

    document.getElementById('open').addEventListener('click', () => {
      //model('block')
      Model('block')
    })
    document.getElementById('close').addEventListener('click', () => {
      //model('none')
      Model('none')
    })
  </script>
</body>
</html>

在上面代码中我们也是使用了闭包用来实现单例模式,因为在我们弹出提示框的时候本质上是创建了一个实例对象展示在页面中,我们想要实现的效果是点击打开就可以打开,点击关闭就可以关闭,如果我们不使用单例模式的话那么每次打开都会创建一个新的实例对象,每次关闭的话同样也是这样的话就不能关闭原来我们想要去关闭的实例对象(失败代码为上面注释的那段代码)。

如果我们想实现打开和关闭都是针对同一个实例对象,那么我们就得使用单例模式进行判断,如果我们打开了弹窗的话,那我们以后关闭也只会对当前这个实例对象进行修改属性,这个特点刚好就符合我们前面所讲的单例模式的应用场景。

结语

在JavaScript中,单例模式是一种设计模式,它确保某个类只有一个实例存在。也就是说,无论你实例化多少次,只会创建一个实例对象。确保全局中只有一个实例存在,有效地节省资源和提高系统的性能。

最后感谢各位大佬们的观看,喜欢的话请三连一下吧。

相关推荐
腾讯TNTWeb前端团队31 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
uhakadotcom4 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom5 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom5 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github