从0开始学习JavaScript--JavaScript 单例模式

news2024/11/19 23:19:42

单例模式是一种常见的设计模式,它保证一个类仅有一个实例,并提供一个全局访问点。在 JavaScript 中,单例模式通常用于创建唯一的对象,以确保全局只有一个实例。本文将深入探讨单例模式的基本概念、实现方式,以及在实际应用中的各种场景。

单例模式的基本概念

单例模式的核心思想是确保一个类只有一个实例,并提供一个全局访问点。这样做的好处包括:

  1. 资源共享: 由于只有一个实例,可以避免多次创建相同对象,减少内存占用。
  2. 全局访问: 通过单一的入口访问对象,方便管理和控制。

在 JavaScript 中,实现单例模式有多种方式,我们将分别介绍其中的三种:懒汉式、饿汉式和模块模式。

懒汉式单例模式

懒汉式单例模式是指在需要时才创建实例,如果实例已经存在,则返回现有实例。这样可以延迟对象的创建,提高性能。

示例代码:

class LazySingleton {
  constructor() {
    if (!LazySingleton.instance) {
      this.data = Math.random(); // 示例中添加随机数,表示实例的一些数据
      LazySingleton.instance = this;
    }

    return LazySingleton.instance;
  }
}

const instance1 = new LazySingleton();
const instance2 = new LazySingleton();

console.log(instance1 === instance2); // 输出: true
console.log(instance1.data === instance2.data); // 输出: true

在这个示例中,LazySingleton 类只有在实例不存在时才创建新实例。之后,无论创建多少次实例,都返回第一次创建的实例。

饿汉式单例模式

饿汉式单例模式是指在应用启动时就立即创建实例,无论后续是否会使用到。

示例代码:

class EagerSingleton {
  constructor() {
    if (!EagerSingleton.instance) {
      this.data = Math.random();
      EagerSingleton.instance = this;
    }

    return EagerSingleton.instance;
  }
}

const instance1 = new EagerSingleton();
const instance2 = new EagerSingleton();

console.log(instance1 === instance2); // 输出: true
console.log(instance1.data === instance2.data); // 输出: true

在这个示例中,EagerSingleton 类在第一次创建实例时就立即创建了一个实例。之后,无论创建多少次实例,都返回第一次创建的实例。

模块模式的单例

模块模式是一种结合了闭包和立即调用函数表达式(IIFE)的方式,创建单例的模式。

示例代码:

const ModuleSingleton = (function () {
  let instance;

  function createInstance() {
    return {
      data: Math.random()
    };
  }

  return {
    getInstance: function () {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

const instance1 = ModuleSingleton.getInstance();
const instance2 = ModuleSingleton.getInstance();

console.log(instance1 === instance2); // 输出: true
console.log(instance1.data === instance2.data); // 输出: true

在这个示例中,ModuleSingleton 使用闭包和 IIFE 创建了一个包含 getInstance 方法的模块。getInstance 方法确保只有一个实例被创建,并提供全局访问点。

单例模式的实际应用场景

1. 管理全局状态

单例模式常用于管理全局状态,确保整个应用中只有一个状态管理实例,例如 Redux 中的 store。

const store = createStore(reducer);

2. 数据缓存

在需要缓存数据的场景,可以使用单例模式确保只有一个缓存实例。

class DataCache {
  constructor() {
    if (!DataCache.instance) {
      this.cache = {};
      DataCache.instance = this;
    }

    return DataCache.instance;
  }

  set(key, value) {
    this.cache[key] = value;
  }

  get(key) {
    return this.cache[key];
  }
}

const cache = new DataCache();
cache.set('user', { name: 'John' });
console.log(cache.get('user')); // 输出: { name: 'John' }

3. 配置管理

在配置管理中,使用单例模式可以确保只有一个配置管理实例,方便全局访问配置信息。

class ConfigurationManager {
  constructor() {
    if (!ConfigurationManager.instance) {
      this.config = { /* 配置信息 */ };
      ConfigurationManager.instance = this;
    }

    return ConfigurationManager.instance;
  }

  getConfig(key) {
    return this.config[key];
  }
}

const configManager = new ConfigurationManager();
console.log(configManager.getConfig('apiUrl')); // 输出: 配置信息中的 apiUrl

单例模式的进阶应用

1. 日志记录器

在应用中使用单例模式创建一个全局的日志记录器,确保只有一个实例记录所有日志信息。

class Logger {
  constructor() {
    if (!Logger.instance) {
      this.logs = [];
      Logger.instance = this;
    }

    return Logger.instance;
  }

  log(message) {
    this.logs.push(message);
    console.log(message);
  }

  printLogs() {
    console.log('All logs:');
    this.logs.forEach(log => console.log(log));
  }
}

const logger = new Logger();
logger.log('Log message 1');
logger.log('Log message 2');

const anotherLogger = new Logger();
console.log(logger === anotherLogger); // 输出: true
anotherLogger.printLogs(); // 输出: Log message 1 \n Log message 2

在这个例子中,Logger 类用于记录应用中的日志信息,通过单例模式确保只有一个全局的日志记录器。

2. 文件系统管理

在需要管理文件系统的应用中,使用单例模式可以确保只有一个实例负责文件系统的操作,避免文件冲突和资源竞争。

class FileSystemManager {
  constructor() {
    if (!FileSystemManager.instance) {
      this.files = [];
      FileSystemManager.instance = this;
    }

    return FileSystemManager.instance;
  }

  createFile(name) {
    this.files.push(name);
    console.log(`File '${name}' created.`);
  }

  listFiles() {
    console.log('Files in the system:');
    this.files.forEach(file => console.log(file));
  }
}

const fileSystem = new FileSystemManager();
fileSystem.createFile('document.txt');
fileSystem.createFile('image.jpg');

const anotherFileSystem = new FileSystemManager();
console.log(fileSystem === anotherFileSystem); // 输出: true
anotherFileSystem.listFiles(); // 输出: document.txt \n image.jpg

在这个例子中,FileSystemManager 类用于管理文件系统,确保只有一个实例负责文件的创建和列举。

3. 数据库连接

在需要管理数据库连接的应用中,使用单例模式可以确保只有一个实例负责数据库的连接,提高性能和资源利用率。

class DatabaseConnection {
  constructor() {
    if (!DatabaseConnection.instance) {
      this.isConnected = false;
      DatabaseConnection.instance = this;
    }

    return DatabaseConnection.instance;
  }

  connect() {
    if (!this.isConnected) {
      console.log('Database connected.');
      this.isConnected = true;
    } else {
      console.log('Already connected to the database.');
    }
  }

  disconnect() {
    if (this.isConnected) {
      console.log('Database disconnected.');
      this.isConnected = false;
    } else {
      console.log('Not connected to the database.');
    }
  }
}

const dbConnection = new DatabaseConnection();
dbConnection.connect();
dbConnection.disconnect();

const anotherDbConnection = new DatabaseConnection();
console.log(dbConnection === anotherDbConnection); // 输出: true
anotherDbConnection.connect(); // 输出: Already connected to the database.

在这个例子中,DatabaseConnection 类用于管理数据库连接,确保只有一个实例负责数据库的连接和断开。

单例模式的性能考虑

尽管单例模式确保只有一个实例存在,但在大型应用中,可能会导致全局状态的集中管理,增加了代码的耦合性。此外,在多线程环境中,需要考虑线程安全性,避免因为竞态条件而导致的问题。

在性能要求较高的场景,可以根据具体需求选择使用懒汉式或饿汉式单例模式。懒汉式能够延迟实例的创建,降低了启动时的负载,但在首次访问时可能会有性能开销。饿汉式则在应用启动时立即创建实例,保证了全局的唯一性,但可能增加了启动时间。

总结

JavaScript 单例模式是一种有力的设计模式,旨在确保一个类仅有一个实例,并提供一个全局访问点。通过懒汉式、饿汉式和模块模式等多种实现方式,开发者可以根据具体场景选择适合的单例模式,使得代码更为灵活和可维护。

在懒汉式中,实例在首次访问时被创建,延迟加载有助于降低启动时的负载。而饿汉式在应用启动时即创建实例,保证了全局唯一性,但可能增加了启动时间。模块模式结合了闭包和IIFE,为单例提供了一种更为模块化和安全的实现方式。

单例模式在实际应用中有着广泛的应用,包括管理全局状态、数据缓存、配置管理等方面。通过确保唯一实例的存在,单例模式提高了代码的可维护性和可读性,使得应用在全局范围内具备更好的控制和管理能力。

然而,开发者在使用单例模式时需要注意全局状态的管理可能带来的代码耦合问题。在性能要求较高的场景,可以选择懒汉式或饿汉式单例,根据具体需求权衡延迟加载和启动时间的取舍。综合而言,JavaScript 单例模式为项目提供了更好的架构和代码组织方式,让代码充满设计模式的智慧。

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

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

相关文章

linux 磁盘扩容初始化挂载 笔记

目录 说明环境信息前提条件 操作步骤 说明 linux 系统磁盘扩容步骤 环境信息 系统信息:Linux version 4.19.90-23.8.v2101.ky10.aarch64cpu信息:Kunpeng-920 、aarch64、64-bit、HiSilicon 前提条件 有未初始化的用户磁盘操作系统可以支持当前磁盘的…

【Spring系列】DeferredResult异步处理

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

VC++调试QT源码

环境:vs2017 qt 5.14.2 1:首先我们需要选择我们的源码路径 右键解决方案-》属性-》通用属性-》调试源文件-》在窗口内添加QT下载时的源码**.src文件夹**,这里最好把源码 D:\software\QT\path\5.14.2\Src 源文件里面的Src文件做一个备份出来…

从意义中恢复,而不是从数据包中恢复

从书报,录放机,电视机到智能手机,vr 眼镜,所有学习的,娱乐的工具或玩具,几乎都以光声诉诸视听,一块屏幕和一个喇叭。 视觉和听觉对任何动物都是收发信息的核心,诉诸视觉和听觉的光和…

达梦数据库使用

达梦数据库使用 📑前言 本文主要是【达梦数据库】——达梦数据库简单使用的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是听风与他🥇 ☁️博客首页:CSDN主页听风与他…

运维知识点-openResty

openResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——实现广告缓存测试企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——OpenResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRes…

机关单位档案分类及整理方法

机关单位档案主要包含文书档案、干部职工档案(人事档案)、会计档案、科技档案(科学研究、基本建设、设备仪器、产品)、诉讼档案、音像档案、照片档案、电子档案等等,这其中,不同种类,不同载体的…

卷积神经网络(CNN)注意力检测

文章目录 一、前言二、前期工作1. 设置GPU(如果使用的是CPU可以忽略这步)2. 导入数据3. 查看数据 二、数据预处理1.加载数据2. 可视化数据4. 配置数据集 三、调用官方网络模型四、设置动态学习率五、编译六、训练模型七、模型评估1. Accuracy与Loss图2. …

ESP32-Web-Server 实战编程- 使用 AJAX 自动更新网页内容

ESP32-Web-Server 实战编程- 使用 AJAX 自动更新网页内容 概述 什么是 AJAX ? AJAX Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。 AJAX 是一种用于创建快速动态网页的技术。 传统的网页(不使用 AJAX&#…

更改AndroidStudio模拟器位置

C盘何等的珍贵,可是好多工具,软件非得默认安装在C盘。。导致C盘越来越紧张。。 在日常使用过程中,安装任何软件都会将其安装到非系统盘下,Android模拟器也不能例外。保护好C盘也是日常一个良好的习惯。 Android AVD默认路径&…

watch监听中重复触发如何解决?

在实际开发工程中通过获取后端数据监听判断数组中长度是否大于0从而调用其他的方法,但是如果data域中的数据出现变化的话,就会导致监听中的方法重复调用,导致一些不必要的bug,例如: 原理: watch监听的数据…

Android性能优化- 从SharedPreferences到MMKV

前言 前面Android性能优化 - 从SharedPreferences跨越到DataStore一文主要介绍了DataStore的实现原理,以及DataStore相对于SharedPreferences的提升,本文主要简述MMKV相对于SharedPreferences存储的使用及优劣势,以及MMKV原理,以…

又3本“On Hold”期刊被剔除!这本Elsevier旗下中科院2区TOP仍在调查中!

【SciencePub学术】 此前,继又2本期刊被“On Hold”!标识后,仍处于“On Hold”状态的期刊有8本,其中包括4本SCI期刊和4本ESCI期刊。 2023年11月20日,科睿唯安更新了Web of Science核心期刊目录。 本次11月更新共64本期…

聚类分析例题 (多元统计分析期末复习)

例一 动态聚类,K-means法,随机选取凝聚点(题目直接给出) 已知5个样品的观测值为:1,4,5,7,11。试用K均值法分为两类(凝聚点分别取1,4与1,11) 解&…

深入探索 Vue 响应式原理:数据驱动视图的奥秘

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

原神:夏洛蒂是否值得培养?全队瞬抬治疗量不输五星,但缺点也很明显

作为四星冰系治疗角色,夏洛蒂的实战表现可以说相当让人惊喜。不仅有相当有意思的普攻动作以及技能特效,而且她还有治疗和挂冰等功能性。下面就来详细聊聊夏洛蒂是否值得培养。 【治疗量让人惊喜,但也有缺点】 说实话,在使用夏洛蒂…

第二部分 系统管理篇

文件和目录管理 Linux基础 在Linux操作系统中,一切都是文件。Linux文件是区分大小写的。 Linux文件的拓展名和它的种类没有任何关系 Linux的目录结构为树状结构,顶级的目录为根目录“/”。 文件类型 用file命令查看文件类型 文件操作命令 1.mkdir创…

深度学习框架:Pytorch与Keras的区别与使用方法

☁️主页 Nowl 🔥专栏《机器学习实战》 《机器学习》 📑君子坐而论道,少年起而行之 文章目录 Pytorch与Keras介绍 Pytorch 模型定义 模型编译 模型训练 输入格式 完整代码 Keras 模型定义 模型编译 模型训练 输入格式 完整代…

GoLong的学习之路,进阶,Redis

这个redis和上篇rabbitMQ一样,在之前我用Java从原理上进行了剖析,这里呢,我做项目的时候,也需要用到redis,所以这里也将去从怎么用的角度去写这篇文章。 文章目录 安装redis以及原理redis概念redis的应用场景有很多red…

机器学习笔记 - 3D数据的常见表示方式

一、简述 从单一角度而自动合成3D数据是人类视觉和大脑的基本功能,这对计算机视觉算法来说是比较难的。但随着LiDAR、RGB-D 相机(RealSense、Kinect)和3D扫描仪等3D传感器的普及和价格的降低,3D 采集技术的最新进展取得了巨大飞跃。与广泛使用的 2D 数据不同,3D 数据具有丰…