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

news/2024/10/25 3:26:26/

单例模式是一种常见的设计模式,它保证一个类仅有一个实例,并提供一个全局访问点。在 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.ppmy.cn/news/1249906.html

相关文章

elasticsearch版本和jdk对应关系

下载地址: https://www.elastic.co/cn/downloads/past-releases#elasticsearch 对应关系 https://www.elastic.co/cn/support/matrix#matrix_jvm

Redis对象系统

前言 在Redis中有许多数据结构,比如:简单动态字符串(SDS),双端链表,字典,压缩列表,整数集合等。 Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统。…

Linux处理文本常见命令

目录 1 vim 2 echo 3 tee 4 cat 1 vim 编辑文本类的内容,使用的时候 vim [文件名],比如 vim A.txt 进入vim界面后,按i可以开启编辑模式,按ESC可以关闭编辑模式,关闭编辑模式后:wq!保存并退出 2 echo ech…

BGP综合实验(IP)

实验要求: 实验思路: 1.划分IP地址: 将172.16.0.0/16的网段划分为172.16.0.0/24的多个网段,因为在实际工程当中,24的网段更符合用户网段,因此先将网段划分为172.16.0.0 /24的多个子网掩码为24的网段&…

图像生成MaaS服务

小红书:http://www.qiyuai.net/apps/met/community/selected 美图:https://www.whee.com/ai/text-to-image 混元:小程序 微软:copilot DALLE3

Flink源码解析零之重要名词的理解

名词解释 1)StreamGraph 根据用户通过 Stream API 编写的代码生成的最初的图。 (1)StreamNode 用来代表 operator 的类,并具有所有相关的属性,如并发度、入边和出边等。 (2)StreamEdge 表示连接两个StreamNode的边。 2)JobGraph StreamGraph经过优化后生成了 J…

zookeeper 客户端常用命令简单记录(实操课程系列--watcher功能测试)(发布订阅功能测试)

本系列是zookeeper相关的实操课程,课程测试环环相扣,请按照顺序阅读测试来学习zookeeper。阅读本文之前,请先阅读----zookeeper 单机伪集群搭建简单记录(实操课程系列) 1、命令行工具切换到zookeeper的bin目录下面&am…

目标检测YOLO系列从入门到精通技术详解100篇-【目标检测】相机标定(附Python代码实现)

目录 前言 算法原理 相机标定内参和外参 相机标定的方法 标定模板