哎,写这玩意就得白话一段,初学js时,只知道写出来的代码能实现出来自己想要的东西,自己就不会过多的去关注其他,但随着做开发时间长了,自己也想去开发点东西,或者去理解一些其他人的代码时,总会有着不清楚,看不懂的尴尬。
初听单例模式时,还是在一次面试,面试官问什么是单例模式,大脑直接宕机了,单例模式,听说过工厂模式,没听说过单例模式啊,尴尬啊,现在其实也不清楚。
趁着这个机会,就一起学习一下,整理一下单例模式,让它彻底转化为我之利刃,为我们所用。
文章目录
什么是单例模式?
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个唯一的实例。
您可以把它想象成:
- 国家的总统:一个国家在任期内只有一位总统。
- 电脑的回收站:无论你从哪个磁盘分区删除文件,打开的都是同一个回收站。
- 任务管理器:一个系统中的任务管理器实例只能有一个,这样才能方便系统去统筹所有的任务。
为什么要用?
在一些场景的前提下,我们需要某个对象,在全局的范围中,有且仅有一个对象实例的存在,这样方便我们全局的访问以及管理。
如果创建了多个实例,会导致一些我们所不期望的问题:
- 资源浪费
- 数据不一致
- 程序执行不统一
特点
优点:
- 保证一个类只有一个实例
- 提供全局访问点
- 避免重复创建对象,节省内存
缺点:
- 违反单一职责原则
- 可能产生隐藏的依赖关系
- 不利于单元测试(难以模拟)
关键实现要点
要实现一个单例,通常需要做到以下两点:
- 私有化构造函数 :防止外部使用
new
关键字随意创建多个实例。 - 提供一个静态方法 (如
getInstance
),该方法负责检查实例是否已经存在:- 如果已存在,则返回这个已有的实例。
- 如果不存在,则创建一个新的实例并返回,同时将其保存下来以备下次使用。
实现方式
1.对象字面量
javascript
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.闭包实现
javascript
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实现
javascript
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); // true
instance1.addItem('item1');
console.log(instance2.getItems()); // ['item1']
模块模式
js
// 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'
适用场景
- 全局配置对象
- 缓存系统
- 日志记录器
- 数据库连接池
- 状态管理器
实际应用场景例子
全局状态管理
javascript
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 });
缓存管理器
javascript
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);
日志管理器
javascript
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());
全局事件总线
javascript
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 的单例模式相比其他语言更灵活。