JavaScript中单例模式这样用

news2025/1/16 5:51:57

如果希望自己的代码更优雅、可维护性更高以及更简洁,往往离不开设计模式这一解决方案。

在JS设计模式中,最核心的思想:封装变化(将变与不变分离,确保变化的部分灵活,不变的部分稳定)。

单例模式

那么来说说第一个常见的设计模式:单例模式

单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问方式,为了解决一个全局使用的类频繁被创建和销毁、占用内存的问题。

ES5中通过闭包

在ES5中,可以使用闭包(函数内部返回函数被外界变量所引用,导致这个函数里面的变量无法被释放,就构建成闭包)来保存这个类的实例。

var Singeton = (function(){
    var instance;
    
    function User(name,age){
        this.name=name;
        this.age=age;
    }
    
    return function(name,age){
        if(!instance){
            instance = new User(name,age)
        }
        return instance
    }
})()

此时这个实例一旦生成,每次都是返回这个实例,且不会被修改,可以看到下面的代码,当给 User 对象初始赋值 name:alice,age:18 时,以后再赋值便无效了,以及每次返回都是初始的实例对象。

在这里插入图片描述

ES6中使用类的静态属性

以上代码使用ES6语法来实现,通过类的静态属性来保存唯一的实例对象。

  class Singeton {
    constructor(name,age){
        if(!Singeton.instance){
            this.name = name;
            this.age = age;
            Singeton.instance = this;
        }

       return Singeton.instance;
    }
}

创建方式仍然是一样的,通过 new 关键字创建类的实例对象。

案例

那这样一种设计模式在开发中实际有什么用途呢?我们试想这样一个业务场景:访问网站时,很久没有操作页面,此时授权过期,当我们点击页面上的任何一个地方,都会弹出一个登录框。

那么这个登录框,是全局唯一的,不会存在多份,也不会互相冲突,所以不需要每次都创建一份,保留初始那一份就够了。

提前创建节点

我们可能会想到首先在页面中提前创建节点,编写好页面样式,最后通过控制元素的 display 属性来达到显示和隐藏的效果。

<div class="modal">登录对话框</div>
<button id="open">打开</button>
<button id="close">关闭</button>

<style>
  .modal {
    display: none;
    /* 其他布局代码省略 */
  }
</style>

<script>
  document.querySelector("#open").onclick = function(){
     const modal = document.querySelector('.modal')
     modal.style.display = 'block'
  }
</script>

这样可以完成需求,全局只有一个登录框,每次都展示同一个。但问题是dom元素从一开始它创建好并添加到body中,无论是否需要用到,如果有些场景不需要登陆,那么这里初始渲染就会浪费空间。

单例模式

那如果不需要初始渲染,仅当需要时才使用,并且每次都返回同一个实例的单例模式应该如何实现呢?

我们可以这样处理

<!-- 去除class为modal的标签,动态创建 -->

<script>
const Modal = (function(){
  let instance = null
  return function(){
      if(!instance){
          instance = document.createElement("div")
          instance.innerHTML = "登录对话框"
          instance.className = "modal"
          instance.style.display = "none"
          document.body.appendChild(instance)
      }
      return instance
  }
})()

document.querySelector("#open").onclick = function(){
      //创建modal,如果放在外面,一开始就会创建元素
      const modal = Modal()    
      //显示modal
      modal.style.display = "block"
  }

  document.querySelector("#close").onclick = function(){
      const modal = Modal()    
      modal.style.display = "none"
  }
</script>

虽然上面的方式可以达到效果,但是创建对象和管理单例的逻辑都放在了对象内部,是有些混乱的。并且如果下次需要创建页面中唯一的 iframe,或者 script 标签,就得将以上函数照抄一遍。

通用单例

首先拆分函数逻辑,将执行创建对象的逻辑拿出来

const createLayer = function(){
  let div = document.createElement("div")
  div.innerHTML = "登录对话框"
  div.className = "modal"
  div.style.display = "none"
  document.body.appendChild(div);
  return div;
}

const Modal = (function(){
  let instance = null
  return function(){
      if(!instance){
          instance = createLayer()
      }
      return instance
  }
})()

以上修改后代码逻辑就更为清晰,但此时还不支持通用化的创建别的组件,这时候我们想想如何将创建单例的方法进行一些优化,是否可以将单例需要执行的函数抽象化。

const createSingle = (function(fn){
    let instance; 
    return function(){
        return instance || ( instance = fn.apply(this, arguments))
    }
})()

这样改造后,如果存在创建 iframe 的方法,也可以直接使用。

const createIframe = function() {
  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  document.body.appendChild(iframe);
  return iframe
}
const singleIframe = createSingle(createIframe)

document.querySelector("#open").onclick = function(){
 const iframe = singleIframe()
 iframe.style.display = 'block'
}

实际应用

以上都是咱小打小闹的试用,那再来看看社区中一些非常棒的实现吧~ 比如:React 中常用的状态管理工具 Redux 就使用到了单例模式,它有这样一些要求。

  • 单一数据源:整个应用的 state 只存在于唯一一个 store 中。
  • State 是只读的:不要直接改变 state 的值,唯一改变 state 的方法就是触发 action。
  • reducer 是纯函数:需要编写纯函数 reducer 来修改 state 的值。

来看看 Redux 的源码,为了便于阅读已删减部分逻辑判断和注释,可以看到通过 store 的 getState 方法每次获取闭包中的 currentState。

在这里插入图片描述

单例模式在内存中只有一个实例,可以减少内存开支,同时还能在系统设置全局的访问点,优化和共享资源。

以上就是单例模式的相关介绍。更多有关 前端设计模式的内容可以参考我其它的博文,持续更新中~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/379066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Batch 综合案例实战中

目录 需求一 需求二 转视频版 需求一 需求&#xff1a;先动态生成50w条员工数据&#xff0c;存放再employee.csv文件中 步骤1&#xff1a;定义&#xff1a;DataInitController RestController public class DataInitController {Autowiredprivate IEmployeeService emplo…

arduino-sentry2之卡片篇

欧克,今天在学生的强烈要求下 我又重启arduino的sentry2调试篇 目前实验结果,可以检测到10张交通卡片 也就是如图所示十张 具体视频如下: https://live.csdn.net/v/279170 具体代码如下: #include <Arduino.h> #include <

什么是千年虫?计算机如何开始处理日期?都有哪些时间日期格式化?

目录 “千年虫”漏洞&#xff08;Year 2000 Problem&#xff0c;简称“Y2K”&#xff09; 计算机是怎么开始处理日期的么&#xff1f; 举例1&#xff1a;时间格式化举例( 过滤器) 举例2&#xff1a;时间格式化 自定义私有过滤器(日期格式化) 高性能计数器演示 OLE时间对象…

Vue的组件(注册、局部、组件复用、props、emit、生命周期)全解

文章目录前言知识点组件注册局部组件组件复用组件间通信props 类型检测子父组件通信之 emit动态组件生命周期函数前言 Vue 支持模块化和组件化开发&#xff0c;可以将整个页面进行模块化分割&#xff0c;低耦合高内聚&#xff0c;使得代码可以在各个地方使用。 知识点 组件注册…

python自学之《21天学通Python》(15)——第18章 数据结构基础

数据结构是用来描述一种或多种数据元素之间的特定关系&#xff0c;算法是程序设计中对数据操作的描述&#xff0c;数据结构和算法组成了程序。对于简单的任务&#xff0c;只要使用编程语言提供的基本数据类型就足够了。而对于较复杂的任务&#xff0c;就需要使用比基本的数据类…

华三OSPF 综合实验

OSPF 实验 实验拓扑 实验需求 按照图示配置 IP 地址按照图示分区域配置 OSPF &#xff0c;实现全网互通为了路由结构稳定&#xff0c;要求路由器使用环回口作为 Router-id&#xff0c;ABR 的环回口宣告进骨干区域 实验解法 1.配置 IP 地址部分 2.按照图示分区域配置 OS…

FFmpeg从入门到入魔(1):初探FFmpeg框架

1. FFmpeg介绍与裁剪1.1 FFmpeg简介FFmpeg&#xff08;Fast forword mpeg&#xff0c;音视频转换器&#xff09;是一个开源免费跨平台的视频和音频流方案&#xff0c;它提供了录制/音视频编解码、转换以及流化音视频的完整解决方案。ffmpeg4.0.2源码目录结构如下&#xff1a;目…

为什么IBDP的文凭更受美国大学的青睐?

家长们可以看到&#xff0c;不管是AP还是A-LEVEL这样的课程&#xff0c;都只是单科的课程&#xff08;A-LEVEL也是英国发展出来&#xff0c;AP是针对美国大学设计的&#xff09;&#xff0c;学生是可以针对他们的强项去做选修&#xff0c;比如我的化学很强&#xff0c;那我可以…

第十节 集合

集合 什么是集合 集合就是能储存一批元素的容器。 特征&#xff1a; 集合类型可以不固定&#xff0c;大小也是可变的。 ArrayList集合 ArrayList是集合中的一种&#xff0c;它支持索引。 ArrayList集合的对象获取 public ArrayList()创建一个空的集合对象 ArrayList集合的添加…

Hive 一文读懂

Hive 简介1.1 什么是Hive1&#xff09;hive简介Hive&#xff1a;由Facebook开源用于解决海量结构化日志的数据统计。Hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能。2&#xff09;Hive本质&#xff1a;将…

Goframe快速创建项目,并使用Cli工具创建dao、service、logic

GoFrame项目创建与Cli工具创建1.项目创建2.Mysql数据库配置3.Cli工具dao自动生成4.业务模型须知5.Cli工具service/logic自动生成 - 接口6.Controller/Api创建1.项目创建 官网 - 项目创建-init 开发文档 - 目录介绍 官网 - 示例项目 1.gf init 项目名 &#xff08;创建项目…

无法定位程序输入点kernel32.dll,如何修复kernel32.dll

kernel32.dll是Windows操作系统中非常重要的一个系统文件&#xff0c;如果它丢失或损坏可能会导致许多应用程序无法正常运行。今天小编就来给大家详细的讲解一下无法定位程序输入点kernel32.dll&#xff0c;我们要怎么修复这个kernel32.dll缺失的问题。 一.kernel32.dll时候什么…

前端开发环境配置,浏览器跨域配置,代码提交配置git等

这是我目前公司的开发配置文档大家可以参考&#xff1a; 前端文档 1 搭建前端环境 1.1 安装nodejs 1.1.1 nodejs下载地址 https://nodejs.org/dist/v10.15.3/node-v10.15.3-x64.msi&#xff08;win64&#xff09; https://nodejs.org/dist/v10.15.3/node-v10.15.3.pkg&…

查询性能较 Trino/Presto 3-10 倍提升!Apache Doris 极速数据湖分析深度解读

从上世纪 90 年代初 Bill Inmon 在《building the Data Warehouse》一书中正式提出数据仓库这一概念&#xff0c;至今已有超过三十年的时间。在最初的概念里&#xff0c;数据仓库被定义为「一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合&#xff0c;用于支持管理…

Python排序 -- 内附蓝桥题:错误票据,奖学金

排序 ~~不定时更新&#x1f383;&#xff0c;上次更新&#xff1a;2023/02/28 &#x1f5e1;常用函数&#xff08;方法&#xff09; 1. list.sort() --> sort 是 list 的方法&#xff0c;会直接修改 list 举个栗子&#x1f330; li [2,3,1,5,4] li.sort() print(li) …

New Bing怼人、说谎、PUA,ChatGPT已经开始胡言乱语了

最近&#xff0c;来自大洋彼岸那头的ChatGPT科技浪潮席卷而来&#xff0c;微软将chatGPT整合搜索引擎Bing开启内测后&#xff0c;数百万用户蜂拥而至&#xff0c;都想试试这个「百事通」。 赶鸭子上架&#xff0c;“翻车”了&#xff1f; 但短短上线十几天&#xff0c;嵌入了…

5个开源的Java项目快速开发脚手架

概览 &#xff1a; GunspigRuoYiJeecg-bootiBase4J 一、Guns 推荐指数 &#xff1a;⭐⭐⭐⭐⭐ 简介 采用主流框架 &#xff1a; 基于 Spring Boot2.0版本开发&#xff0c;并且支持 Spring Cloud Alibaba 微服务。功能齐全 &#xff1a;包含系统管理&#xff0c;代码生成&a…

python线程池【ThreadPoolExecutor()】批量获取博客园标题数据

转载&#xff1a;蚂蚁学python 网址&#xff1a;【【2021最新版】Python 并发编程实战&#xff0c;用多线程、多进程、多协程加速程序运行】 https://www.bilibili.com/video/BV1bK411A7tV/?p8&share_sourcecopy_web&vd_sourced0ef3d08fdeef1740bab49cdb3e96467实战案…

SpringMVC 面试题

1、什么是SpringMVC&#xff1f; SpringMVC是一个基于Java的实现了MVC设计模式的“请求驱动型”的轻量级WEB框架&#xff0c;通过把model&#xff0c;view&#xff0c;controller 分离&#xff0c;将web层进行职责的解耦&#xff0c;把复杂的web应用分成逻辑清晰的几个部分&am…

Arduino-PWM调光

PWM调光实验什么是PWM&#xff1f;PWM是&#xff08;Pulse Width Modulation&#xff09;的简称&#xff0c;中文我们说这是脉冲宽度调制。脉冲宽度调制是一种模拟控制方式&#xff0c;根据相应载荷的变化来调制晶体管基极或MOS管栅极的偏置&#xff0c;来实现晶体管或MOS管导通…