「前端每日一问(32)」如何用闭包实现一个单例模式?

990 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

本题难度:⭐ ⭐

答:

function Singleton () {}

const getSingletonInstance = (function () {
  let instance = null
  return function () {
    if (!instance) {
      instance = new Singleton()
    }
    return instance
  }
})()

const s1 = getSingletonInstance()
const s2 = getSingletonInstance()

console.log(s1 === s2) // true

什么是单例模式

单例模式是设计模式之一,它保证一个类仅有一个实例,并提供一个访问它的全局访问点

单例模式的好处就是避免了重复实例化带来的内存开销。

如何才能保证一个类仅有一个实例?

class Singleton {}

const s1 = new Singleton()
const s2 = new Singleton()

console.log(s1 === s2) // false

上面的代码中,s1 和 s2 是两个不同的实例,指向不同的内存地址。

而单例模式想要做到的是,不管我们尝试去 new 多少次 Singleton 这个类,它都只返回第一次创建的那个实例

用一个静态属性 instance 来记录生成的实例,用一个 getInstance 方法来判断实例是否存在,如果存在就直接返回,否则就创建了再返回。

class Singleton {
  static instance = null
  static getInstance () {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton()
    }
    return Singleton.instance
  }
}

const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()

console.log(s1 === s2) // true

闭包实现单例模式

也可以用闭包来实现单例模式,用一个私有变量 instance 来记录实例

function Singleton () {}

const getSingletonInstance = (function () {
  let instance = null
  return function () {
    if (!instance) {
      instance = new Singleton()
    }
    return instance
  }
})()

const s1 = getSingletonInstance()
const s2 = getSingletonInstance()

console.log(s1 === s2) // true

image.png

这样就把第一次创建的实例保存到了闭包中的 instance 变量里,后续无论再创建多少次,直接返回就行。

单例模式的应用:实现一个全局唯一的模态框

单例模式模态框.gif

<!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>
    .modal {
      width: 200px;
      height: 200px;
      border: 1px solid rgb(67, 69, 160);
      position: fixed;
      top: 30%;
      left: 50%;
      transform: translate(-50%, -50%);
      text-align: center;
    }
  </style>
</head>
<body>
  <button id="openBtn">打开模态框</button>
  <button id="closeBtn">关闭模态框</button>
  <script>
    const Modal = (function() {
      let modal = null
      return function() {
        if (!modal) {
          modal = document.createElement('div')
          modal.innerHTML = '我是全局唯一的模态框'
          modal.className = 'modal'
          modal.style.display = 'none'
          document.body.appendChild(modal)
        }
        return modal
      }
    })()
    
    // 点击打开按钮展示模态框
    document.getElementById('openBtn').addEventListener('click', function() {
    	const modal = new Modal()
    	modal.style.display = 'block'
    })
    
    // 点击关闭按钮隐藏模态框
    document.getElementById('closeBtn').addEventListener('click', function() {
    	const modal = new Modal() // 重新定义一次,其实还是同一个模态框
    	if(modal) {
    	  modal.style.display = 'none'
    	}
    })
  </script>
</body>
</html>

image.png

结尾

如果我的文章对你有帮助,你的👍就是对我的最大支持^_^

你也可以关注《前端每日一问》这个专栏,防止失联哦~

我是阿林,输出洞见技术,再会!

上一篇:

「前端每日一问(31)」如何用闭包设计一个缓存模块?

下一篇:

「前端每日一问(33)」闭包与柯里化函数、偏函数的关系?