JS 设计模式: 闭包与单例模式

前言

闭包是一个很重要的概念,也是一个很难理解的概念,这里我就不多说了,如果你还不了解闭包,可以看看这篇文章:简单了解 JavaScript 闭包

什么是单例模式

单例模式是一种常用的软件设计模式,它保证我们系统中的某一个类在任何情况下都绝对只有一个实例,这样有利于我们在系统中共享资源。这种模式通常在需要严格控制某个资源或限制系统中某个特定部分的实例数量时使用。单例模式在许多软件应用中都有广泛应用,例如配置管理、数据库连接、日志记录等。

单例模式的特征

  1. 全局唯一实例: 单例模式确保一个类只有一个实例对象。
  2. 延迟实例化: 实例对象只在第一次请求时创建,之后的请求都返回相同的实例。
  3. 全局访问点: 提供一个访问实例的全局入口,使得任何地方都能够获取到同一个实例。

我们可以通过闭包来实现单例模式,也可以通过构造函数等方式来实现单例模式,下面我们就来看看如何实现。

1. 闭包实现

js 复制代码
function Singleton() {
  let instance = null
  return function() {
    if (!instance) {
      instance = new Object()
    }
    return instance
  }
}

const getInstance = Singleton()

const instance1 = getInstance()
const instance2 = getInstance()

console.log(instance1 === instance2) // true

2. 构造函数实现

js 复制代码
function Singleton() {
  if (typeof Singleton.instance === 'object') {
    return Singleton.instance
  }
  Singleton.instance = this
}

const instance1 = new Singleton()
const instance2 = new Singleton()

console.log(instance1 === instance2) // true

3. ES6 实现

js 复制代码
class Singleton {
  constructor() {
    if (typeof Singleton.instance === 'object') {
      return Singleton.instance
    }
    Singleton.instance = this
  }
}

const instance1 = new Singleton()
const instance2 = new Singleton()

console.log(instance1 === instance2) // true

闭包与单例模式的结合

利用闭包实现单例模式的优点是:可以延迟创建实例,只有在第一次调用时才会创建实例,这样可以节省内存空间。但是这种方式也有缺点,由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以在实际开发中,我们要根据实际情况来选择使用。

比如我们可以来实现一个登录框,我们只需要一个登录框,不需要每次都创建一个新的登录框,这样就可以使用单例模式来实现。

在这个动画中我们可以看到我们无论点击了多少次 打开弹框打开弹框2 按钮,都只会创建并插入一个 DOM 元素,也就是我们的登录框(模态框),并且在点击 关闭弹框 后再次打开弹框时也不会销毁原 DOM 元素。

具体实现

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录框</title>
    <style>
        #modal {
            width: 200px;
            height: 200px;
            line-height: 200px;
            background-color: #ccc;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            border: 1px solid black;
            text-align: center;
        }
    </style>
</head>

<body>
    <button id="open">打开弹框</button>
    <button id="close">关闭弹框</button>
    <button id="open2">打开弹框2</button>

    <script>
        // 立即执行函数
        const Modal = (function () {
            // 创建闭包
            let modal = null

            return function () {
                if (!modal) {
                    modal = document.createElement('div')
                    modal.innerHTML = '我是一个全局唯一的登录框'
                    modal.style.display = 'none'
                    modal.id = 'modal'
                    document.body.appendChild(modal)
                }
                return modal
            }
        })()

        document.getElementById('open').addEventListener('click', function () {
            const modal = new Modal()
            modal.style.display = 'block'
        })

        document.getElementById('open2').addEventListener('click', function () {
            const modal = new Modal()
            modal.style.display = 'block'
        })

        document.getElementById('close').addEventListener('click', function () {
            const modal = new Modal()
            modal.style.display = 'none'
        })
    </script>
</body>

</html>

我们可以来分析一下,为什么我们点击 打开弹框打开弹框2 按钮时都只会创建一个 DOM 元素,而不会创建多个 DOM 元素。

首先我们在 Modal 函数中创建了一个闭包,这个闭包中保存了一个变量 modal,这个变量在闭包中是一直存在的,不会被销毁,所以我们在第一次调用 Modal 函数时,会创建一个 DOM 元素并插入到页面中,然后将这个 DOM 元素赋值给 modal 变量,这样 modal 变量就指向了这个 DOM 元素,当我们再次调用 Modal 函数时,由于闭包中的 modal 变量已经存在,所以不会再次创建 DOM 元素,而是直接返回闭包中的 modal 变量,这样我们就可以在任何地方都可以获取到这个 DOM 元素,从而实现了单例模式。

总结

本文主要介绍了闭包与单例模式的结合,通过闭包实现单例模式的优点是:可以延迟创建实例,只有在第一次调用时才会创建实例,这样可以节省内存空间。但是这种方式也有缺点,由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以在实际开发中,我们要根据实际情况来选择使用。

相关推荐
月弦笙音2 分钟前
【monorepo架构】前端 pnpm workspace详解
前端
小嘿前端仔4 分钟前
React 19 正式发布:这一次,表单和服务器组件终于"原生"了
前端
叫我一声阿雷吧22 分钟前
JS 入门通关手册(48):本地存储全解析(localStorage/sessionStorage/cookie,面试高频)
javascript·本地存储·cookie·localstorage·存储方案· 前端面试·essionstorage
英俊潇洒美少年30 分钟前
前端组件化开发最佳实践 + 高频面试题(Vue & React)
前端·vue.js·react.js
凌览32 分钟前
别再手搓 Skill 了,用这个工具 5 分钟搞定
前端·后端
zero159735 分钟前
TypeScript 快速实战系列:函数进阶|TypeScript 函数 + 异步:大模型 API 调用核心
前端·typescript·大模型编程语言
無名路人37 分钟前
用 codex AI 更新了下之前写的浏览器云书签标签页扩展
前端·openai·ai编程
月弦笙音42 分钟前
【pnpm 】pnpm 执行 xxx 的 底层原理
前端
玄空z1 小时前
通俗理解 RAG 与微调:给大模型“翻书”还是“洗脑”
javascript
Devin_chen1 小时前
单例模式渐进式学习指南
前端·javascript