【JS】什么是单例模式
哎,写这玩意就得白话一段,初学js时,只知道写出来的代码能实现出来自己想要的东西,自己就不会过多的去关注其他,但随着做开发时间长了,自己也想去开发点东西,或者去理解一些其他人的代码时,总会有着不清楚,看不懂的尴尬。
初听单例模式时,还是在一次面试,面试官问什么是单例模式,大脑直接宕机了,单例模式,听说过工厂模式,没听说过单例模式啊,尴尬啊,现在其实也不清楚。
趁着这个机会,就一起学习一下,整理一下单例模式,让它彻底转化为我之利刃,为我们所用。
文章目录
- 什么是单例模式?
- 为什么要用?
- 特点
- 关键实现要点
- 实现方式
- 1.对象字面量
- 2.闭包实现
- 3.Class实现
- 模块模式
- 适用场景
- 实际应用场景例子
- 全局状态管理
- 缓存管理器
- 日志管理器
- 全局事件总线
- 总结
什么是单例模式?
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个唯一的实例。
您可以把它想象成:
- 国家的总统:一个国家在任期内只有一位总统。
- 电脑的回收站:无论你从哪个磁盘分区删除文件,打开的都是同一个回收站。
- 任务管理器:一个系统中的任务管理器实例只能有一个,这样才能方便系统去统筹所有的任务。
为什么要用?
在一些场景的前提下,我们需要某个对象,在全局的范围中,有且仅有一个对象实例的存在,这样方便我们全局的访问以及管理。
如果创建了多个实例,会导致一些我们所不期望的问题:
- 资源浪费
- 数据不一致
- 程序执行不统一
特点
优点:
- 保证一个类只有一个实例
- 提供全局访问点
- 避免重复创建对象,节省内存
缺点:
- 违反单一职责原则
- 可能产生隐藏的依赖关系
- 不利于单元测试(难以模拟)
关键实现要点
要实现一个单例,通常需要做到以下两点:
- 私有化构造函数:防止外部使用
new
关键字随意创建多个实例。 - 提供一个静态方法(如
getInstance
),该方法负责检查实例是否已经存在:- 如果已存在,则返回这个已有的实例。
- 如果不存在,则创建一个新的实例并返回,同时将其保存下来以备下次使用。
实现方式
1.对象字面量
const Singleton = {instanceId:Math.random(),//使用instanceid作为单例的唯一标志config: {apiUrl: 'https://api.example.com',timeout: 5000},getData() {console.log('Getting data from:', this.config.apiUrl);},updateConfig(newConfig) {this.config = { ...this.config, ...newConfig };}
}// 使用
Singleton.getData();
Singleton.updateConfig({ timeout: 3000 });
2.闭包实现
const Singleton = (function() {let instance;function createInstance() {const object = {counter: 0,increment() {this.counter++;console.log('Counter:', this.counter);},getCounter() {return this.counter;}};return object;}return {getInstance() {if (!instance) {instance = createInstance();}return instance;}};
})();// 使用
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();instance1.increment(); // Counter: 1
instance2.increment(); // Counter: 2
console.log(instance1 === instance2); // true
3.Class实现
class Singleton {static instance;constructor() {if (Singleton.instance) {return Singleton.instance;}this.data = [];this.createdAt = new Date();Singleton.instance = this;}addItem(item) {this.data.push(item);}getItems() {return this.data;}static getInstance() {if (!Singleton.instance) {Singleton.instance = new Singleton();}return Singleton.instance;}
}// 使用
const instance1 = new Singleton();
const instance2 = new Singleton();
const instance3 = Singleton.getInstance();console.log(instance1 === instance2); // true
console.log(instance1 === instance3); // trueinstance1.addItem('item1');
console.log(instance2.getItems()); // ['item1']
模块模式
// configManager.js
let instance = null;
let config = {};class ConfigManager {constructor() {if (instance) {return instance;}this.defaultConfig = {theme: 'dark',language: 'zh-CN',apiBaseUrl: 'https://api.example.com'};instance = this;}setConfig(newConfig) {config = { ...this.defaultConfig, ...newConfig };}getConfig(key) {return key ? config[key] : config;}reset() {config = { ...this.defaultConfig };}
}export default new ConfigManager();// 使用
import configManager from './configManager.js';configManager.setConfig({ theme: 'light' });
console.log(configManager.getConfig('theme')); // 'light'
适用场景
- 全局配置对象
- 缓存系统
- 日志记录器
- 数据库连接池
- 状态管理器
实际应用场景例子
全局状态管理
class GlobalState {static instance;constructor() {if (GlobalState.instance) {return GlobalState.instance;}this.state = {};this.listeners = new Map();GlobalState.instance = this;}setState(key, value) {this.state[key] = value;this.notify(key, value);}getState(key) {return this.state[key];}subscribe(key, callback) {if (!this.listeners.has(key)) {this.listeners.set(key, new Set());}this.listeners.get(key).add(callback);// 返回取消订阅函数return () => {this.listeners.get(key)?.delete(callback);};}notify(key, value) {this.listeners.get(key)?.forEach(callback => {callback(value);});}
}// 使用
const stateManager = new GlobalState();// 订阅状态变化
const unsubscribe = stateManager.subscribe('user', (user) => {console.log('User updated:', user);
});stateManager.setState('user', { name: 'John', age: 30 });
缓存管理器
class CacheManager {static instance;constructor() {if (CacheManager.instance) {return CacheManager.instance;}this.cache = new Map();this.ttl = new Map(); // 生存时间CacheManager.instance = this;}set(key, value, ttlMs = 60000) {this.cache.set(key, value);this.ttl.set(key, Date.now() + ttlMs);// 自动清理过期缓存setTimeout(() => {if (this.ttl.get(key) <= Date.now()) {this.delete(key);}}, ttlMs);}get(key) {const expiry = this.ttl.get(key);if (expiry && expiry <= Date.now()) {this.delete(key);return null;}return this.cache.get(key);}delete(key) {this.cache.delete(key);this.ttl.delete(key);}clear() {this.cache.clear();this.ttl.clear();}
}// 使用
const cache = new CacheManager();
cache.set('user-data', { id: 1, name: 'Alice' }, 5000);setTimeout(() => {console.log(cache.get('user-data')); // null (已过期)
}, 6000);
日志管理器
const Logger = (function() {let instance;let logs = [];class LoggerClass {constructor() {if (instance) {return instance;}instance = this;}log(level, message) {const logEntry = {timestamp: new Date().toISOString(),level,message};logs.push(logEntry);console[level](`[${logEntry.timestamp}] ${message}`);}info(message) {this.log('info', message);}error(message) {this.log('error', message);}warn(message) {this.log('warn', message);}getLogs() {return [...logs];}clear() {logs = [];}}return new LoggerClass();
})();// 使用
Logger.info('Application started');
Logger.error('Something went wrong');
console.log(Logger.getLogs());
全局事件总线
class EventBus {static instance;constructor() {if (EventBus.instance) {return EventBus.instance;}this.events = {};EventBus.instance = this;}on(event, callback) {if (!this.events[event]) {this.events[event] = [];}this.events[event].push(callback);}emit(event, data) {if (this.events[event]) {this.events[event].forEach(callback => callback(data));}}
}// 不同模块间通信
const eventBus = new EventBus();// 用户模块
eventBus.on('userLogin', (user) => {console.log('用户登录:', user.name);
});// 购物车模块
eventBus.on('userLogin', (user) => {console.log('加载用户购物车');
});
总结
特点 | 描述 |
---|---|
目的 | 控制实例数目,节省资源,保证全局一致性。 |
核心 | 一个类只有一个实例。 |
实现 | 私有构造器 + 静态获取方法。 |
用途 | 全局配置、日志记录、数据库连接池、对话框、缓存系统等。 |
希望这个解释能帮助您彻底理解单例模式!它是一个在软件开发中非常基础且实用的模式,同时,JavaScript 的单例模式相比其他语言更灵活。