Search K
Appearance
Appearance
📊 SEO元描述:2024年最新JavaScript单例模式教程,详解单例模式实现方式、应用场景分析、模块单例模式。包含完整实战案例,适合前端开发者掌握创建型设计模式。
核心关键词:JavaScript单例模式2024、单例设计模式、JavaScript设计模式、单例模式实现、模块单例模式、前端设计模式
长尾关键词:JavaScript单例模式怎么实现、单例模式应用场景、JavaScript设计模式最佳实践、前端单例模式优缺点、模块化单例模式实现
通过本节JavaScript单例模式完整指南,你将系统性掌握:
单例模式是什么?这是设计模式中最基础也是最重要的创建型模式之一。单例模式(Singleton Pattern)是指确保一个类只有一个实例,并提供全局访问点的设计模式,也是软件架构设计的重要组成部分。
💡 设计模式建议:单例模式是23种设计模式中使用频率最高的模式之一,在前端开发中广泛应用于配置管理、缓存系统、日志记录等场景。
单例模式有多种实现方式,每种方式都有其特点和适用场景。
// 🎉 基础单例模式实现
class BasicSingleton {
constructor() {
// 如果实例已存在,返回现有实例
if (BasicSingleton.instance) {
return BasicSingleton.instance;
}
// 初始化实例
this.timestamp = Date.now();
this.data = new Map();
// 保存实例引用
BasicSingleton.instance = this;
return this;
}
// 获取实例的静态方法
static getInstance() {
if (!BasicSingleton.instance) {
BasicSingleton.instance = new BasicSingleton();
}
return BasicSingleton.instance;
}
// 业务方法
setData(key, value) {
this.data.set(key, value);
}
getData(key) {
return this.data.get(key);
}
getAllData() {
return Object.fromEntries(this.data);
}
}
// 🎉 懒汉式单例模式(延迟初始化)
class LazySingleton {
constructor() {
if (LazySingleton.instance) {
return LazySingleton.instance;
}
this.initialized = false;
LazySingleton.instance = this;
return this;
}
// 延迟初始化
init() {
if (!this.initialized) {
console.log('Lazy Singleton initializing...');
this.config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3
};
this.cache = new Map();
this.initialized = true;
}
return this;
}
static getInstance() {
if (!LazySingleton.instance) {
LazySingleton.instance = new LazySingleton();
}
return LazySingleton.instance.init();
}
getConfig(key) {
return this.config[key];
}
setConfig(key, value) {
this.config[key] = value;
}
cache(key, value) {
if (value !== undefined) {
this.cache.set(key, value);
}
return this.cache.get(key);
}
}
// 🎉 饿汉式单例模式(立即初始化)
class EagerSingleton {
constructor() {
if (EagerSingleton.instance) {
return EagerSingleton.instance;
}
// 立即初始化
this.initialize();
EagerSingleton.instance = this;
return this;
}
initialize() {
console.log('Eager Singleton initializing...');
this.startTime = Date.now();
this.eventBus = new Map();
this.subscribers = new Map();
// 设置定时器或其他初始化逻辑
this.heartbeat = setInterval(() => {
this.emit('heartbeat', { timestamp: Date.now() });
}, 10000);
}
static getInstance() {
if (!EagerSingleton.instance) {
EagerSingleton.instance = new EagerSingleton();
}
return EagerSingleton.instance;
}
// 事件发布订阅功能
on(event, callback) {
if (!this.subscribers.has(event)) {
this.subscribers.set(event, []);
}
this.subscribers.get(event).push(callback);
}
off(event, callback) {
if (this.subscribers.has(event)) {
const callbacks = this.subscribers.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
emit(event, data) {
if (this.subscribers.has(event)) {
this.subscribers.get(event).forEach(callback => {
try {
callback(data);
} catch (error) {
console.error('Event callback error:', error);
}
});
}
}
destroy() {
if (this.heartbeat) {
clearInterval(this.heartbeat);
}
this.subscribers.clear();
EagerSingleton.instance = null;
}
}
// 🎉 线程安全的单例模式(使用闭包)
const ThreadSafeSingleton = (function() {
let instance = null;
let isCreating = false;
function Singleton() {
// 防止通过new直接创建
if (isCreating === false) {
throw new Error('Cannot construct singleton');
}
this.id = Math.random().toString(36).substr(2, 9);
this.createdAt = new Date();
this.resources = new Map();
}
Singleton.prototype.addResource = function(key, resource) {
this.resources.set(key, resource);
};
Singleton.prototype.getResource = function(key) {
return this.resources.get(key);
};
Singleton.prototype.removeResource = function(key) {
return this.resources.delete(key);
};
Singleton.prototype.getInfo = function() {
return {
id: this.id,
createdAt: this.createdAt,
resourceCount: this.resources.size
};
};
return {
getInstance: function() {
if (instance === null) {
isCreating = true;
instance = new Singleton();
isCreating = false;
}
return instance;
},
hasInstance: function() {
return instance !== null;
},
destroyInstance: function() {
if (instance) {
instance.resources.clear();
instance = null;
}
}
};
})();
// 🎉 高级单例模式 - 支持参数化创建
class ParameterizedSingleton {
constructor(config = {}) {
const key = JSON.stringify(config);
// 基于配置参数创建不同的实例
if (ParameterizedSingleton.instances.has(key)) {
return ParameterizedSingleton.instances.get(key);
}
this.config = {
name: 'default',
version: '1.0.0',
debug: false,
...config
};
this.initialize();
ParameterizedSingleton.instances.set(key, this);
return this;
}
static instances = new Map();
initialize() {
this.logger = this.createLogger();
this.storage = this.createStorage();
this.metrics = {
created: Date.now(),
operations: 0,
errors: 0
};
if (this.config.debug) {
console.log(`Singleton created with config:`, this.config);
}
}
createLogger() {
return {
log: (message, ...args) => {
if (this.config.debug) {
console.log(`[${this.config.name}]`, message, ...args);
}
},
error: (message, ...args) => {
console.error(`[${this.config.name}]`, message, ...args);
this.metrics.errors++;
}
};
}
createStorage() {
const storageKey = `singleton_${this.config.name}`;
return {
get: (key) => {
try {
const data = localStorage.getItem(`${storageKey}_${key}`);
return data ? JSON.parse(data) : null;
} catch (error) {
this.logger.error('Storage get error:', error);
return null;
}
},
set: (key, value) => {
try {
localStorage.setItem(`${storageKey}_${key}`, JSON.stringify(value));
this.metrics.operations++;
return true;
} catch (error) {
this.logger.error('Storage set error:', error);
return false;
}
},
remove: (key) => {
try {
localStorage.removeItem(`${storageKey}_${key}`);
this.metrics.operations++;
return true;
} catch (error) {
this.logger.error('Storage remove error:', error);
return false;
}
}
};
}
static getInstance(config = {}) {
return new ParameterizedSingleton(config);
}
static getInstanceByName(name) {
for (const [key, instance] of ParameterizedSingleton.instances) {
const config = JSON.parse(key);
if (config.name === name) {
return instance;
}
}
return null;
}
static getAllInstances() {
return Array.from(ParameterizedSingleton.instances.values());
}
static clearInstances() {
ParameterizedSingleton.instances.clear();
}
getMetrics() {
return {
...this.metrics,
uptime: Date.now() - this.metrics.created
};
}
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
this.logger.log('Config updated:', this.config);
}
}
// 使用示例
console.log('=== 基础单例模式测试 ===');
const singleton1 = new BasicSingleton();
const singleton2 = BasicSingleton.getInstance();
console.log('Same instance:', singleton1 === singleton2); // true
singleton1.setData('user', { name: 'John', age: 30 });
console.log('Data from singleton2:', singleton2.getData('user'));
console.log('=== 懒汉式单例模式测试 ===');
const lazySingleton1 = LazySingleton.getInstance();
const lazySingleton2 = LazySingleton.getInstance();
console.log('Lazy singletons same:', lazySingleton1 === lazySingleton2); // true
lazySingleton1.setConfig('apiUrl', 'https://new-api.example.com');
console.log('Config from lazy2:', lazySingleton2.getConfig('apiUrl'));
console.log('=== 饿汉式单例模式测试 ===');
const eagerSingleton = EagerSingleton.getInstance();
eagerSingleton.on('heartbeat', (data) => {
console.log('Heartbeat received:', data.timestamp);
});
console.log('=== 线程安全单例模式测试 ===');
const threadSafeSingleton = ThreadSafeSingleton.getInstance();
threadSafeSingleton.addResource('database', { connection: 'active' });
console.log('Thread safe singleton info:', threadSafeSingleton.getInfo());
console.log('=== 参数化单例模式测试 ===');
const paramSingleton1 = ParameterizedSingleton.getInstance({ name: 'app1', debug: true });
const paramSingleton2 = ParameterizedSingleton.getInstance({ name: 'app2', debug: false });
const paramSingleton3 = ParameterizedSingleton.getInstance({ name: 'app1', debug: true });
console.log('Param singleton1 === singleton3:', paramSingleton1 === paramSingleton3); // true
console.log('Param singleton1 === singleton2:', paramSingleton1 === paramSingleton2); // false
paramSingleton1.storage.set('test', { value: 123 });
console.log('Storage test:', paramSingleton1.storage.get('test'));单例模式的典型应用场景包括配置管理、缓存系统、日志记录、数据库连接池等需要全局唯一实例的场景:
// 🎉 实际应用场景示例
// 1. 配置管理器
class ConfigManager {
constructor() {
if (ConfigManager.instance) {
return ConfigManager.instance;
}
this.config = {};
this.watchers = new Map();
this.loadConfig();
ConfigManager.instance = this;
return this;
}
async loadConfig() {
try {
// 从多个来源加载配置
const defaultConfig = await this.loadDefaultConfig();
const envConfig = await this.loadEnvironmentConfig();
const userConfig = await this.loadUserConfig();
this.config = {
...defaultConfig,
...envConfig,
...userConfig
};
this.notifyWatchers('config-loaded', this.config);
} catch (error) {
console.error('Failed to load config:', error);
}
}
async loadDefaultConfig() {
return {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
theme: 'light',
language: 'en'
};
}
async loadEnvironmentConfig() {
// 从环境变量或环境配置文件加载
return {
apiUrl: process.env.API_URL || 'https://api.example.com',
debug: process.env.NODE_ENV === 'development'
};
}
async loadUserConfig() {
// 从用户设置或本地存储加载
try {
const userConfig = localStorage.getItem('userConfig');
return userConfig ? JSON.parse(userConfig) : {};
} catch {
return {};
}
}
get(key, defaultValue = null) {
return this.config[key] !== undefined ? this.config[key] : defaultValue;
}
set(key, value) {
const oldValue = this.config[key];
this.config[key] = value;
// 持久化用户配置
this.saveUserConfig();
// 通知观察者
this.notifyWatchers('config-changed', { key, value, oldValue });
}
watch(event, callback) {
if (!this.watchers.has(event)) {
this.watchers.set(event, []);
}
this.watchers.get(event).push(callback);
}
unwatch(event, callback) {
if (this.watchers.has(event)) {
const callbacks = this.watchers.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
notifyWatchers(event, data) {
if (this.watchers.has(event)) {
this.watchers.get(event).forEach(callback => {
try {
callback(data);
} catch (error) {
console.error('Config watcher error:', error);
}
});
}
}
saveUserConfig() {
try {
const userConfig = {
theme: this.config.theme,
language: this.config.language,
// 只保存用户可配置的项目
};
localStorage.setItem('userConfig', JSON.stringify(userConfig));
} catch (error) {
console.error('Failed to save user config:', error);
}
}
static getInstance() {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
}
// 2. 缓存管理器
class CacheManager {
constructor() {
if (CacheManager.instance) {
return CacheManager.instance;
}
this.cache = new Map();
this.ttlMap = new Map();
this.maxSize = 1000;
this.defaultTTL = 300000; // 5分钟
this.startCleanupTimer();
CacheManager.instance = this;
return this;
}
set(key, value, ttl = this.defaultTTL) {
// 检查缓存大小限制
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
this.evictOldest();
}
this.cache.set(key, {
value,
timestamp: Date.now(),
accessCount: 0
});
if (ttl > 0) {
this.ttlMap.set(key, Date.now() + ttl);
}
return this;
}
get(key) {
if (!this.cache.has(key)) {
return null;
}
// 检查TTL
if (this.ttlMap.has(key) && Date.now() > this.ttlMap.get(key)) {
this.delete(key);
return null;
}
const item = this.cache.get(key);
item.accessCount++;
item.lastAccess = Date.now();
return item.value;
}
has(key) {
return this.cache.has(key) &&
(!this.ttlMap.has(key) || Date.now() <= this.ttlMap.get(key));
}
delete(key) {
this.cache.delete(key);
this.ttlMap.delete(key);
return this;
}
clear() {
this.cache.clear();
this.ttlMap.clear();
return this;
}
evictOldest() {
// LRU淘汰策略
let oldestKey = null;
let oldestTime = Date.now();
for (const [key, item] of this.cache) {
const lastAccess = item.lastAccess || item.timestamp;
if (lastAccess < oldestTime) {
oldestTime = lastAccess;
oldestKey = key;
}
}
if (oldestKey) {
this.delete(oldestKey);
}
}
startCleanupTimer() {
setInterval(() => {
this.cleanup();
}, 60000); // 每分钟清理一次
}
cleanup() {
const now = Date.now();
const expiredKeys = [];
for (const [key, expireTime] of this.ttlMap) {
if (now > expireTime) {
expiredKeys.push(key);
}
}
expiredKeys.forEach(key => this.delete(key));
}
getStats() {
return {
size: this.cache.size,
maxSize: this.maxSize,
hitRate: this.calculateHitRate(),
memoryUsage: this.estimateMemoryUsage()
};
}
calculateHitRate() {
let totalAccess = 0;
for (const item of this.cache.values()) {
totalAccess += item.accessCount;
}
return totalAccess > 0 ? (this.cache.size / totalAccess) * 100 : 0;
}
estimateMemoryUsage() {
let size = 0;
for (const [key, item] of this.cache) {
size += key.length * 2; // 字符串大小估算
size += JSON.stringify(item.value).length * 2;
}
return size;
}
static getInstance() {
if (!CacheManager.instance) {
CacheManager.instance = new CacheManager();
}
return CacheManager.instance;
}
}
// 3. 日志管理器
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
this.maxLogs = 1000;
this.logLevel = 'info';
this.outputs = ['console'];
Logger.instance = this;
return this;
}
setLevel(level) {
this.logLevel = level;
return this;
}
addOutput(output) {
if (!this.outputs.includes(output)) {
this.outputs.push(output);
}
return this;
}
log(level, message, ...args) {
const logEntry = {
timestamp: new Date().toISOString(),
level,
message,
args,
stack: new Error().stack
};
// 添加到内存日志
this.logs.push(logEntry);
if (this.logs.length > this.maxLogs) {
this.logs.shift();
}
// 输出到各个目标
this.outputs.forEach(output => {
this.outputLog(output, logEntry);
});
}
outputLog(output, logEntry) {
const { timestamp, level, message, args } = logEntry;
const formattedMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
switch (output) {
case 'console':
console[level] ? console[level](formattedMessage, ...args) :
console.log(formattedMessage, ...args);
break;
case 'localStorage':
this.saveToLocalStorage(logEntry);
break;
case 'server':
this.sendToServer(logEntry);
break;
}
}
saveToLocalStorage(logEntry) {
try {
const logs = JSON.parse(localStorage.getItem('app_logs') || '[]');
logs.push(logEntry);
if (logs.length > 100) {
logs.splice(0, logs.length - 100);
}
localStorage.setItem('app_logs', JSON.stringify(logs));
} catch (error) {
console.error('Failed to save log to localStorage:', error);
}
}
async sendToServer(logEntry) {
try {
await fetch('/api/logs', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(logEntry)
});
} catch (error) {
console.error('Failed to send log to server:', error);
}
}
debug(message, ...args) {
this.log('debug', message, ...args);
}
info(message, ...args) {
this.log('info', message, ...args);
}
warn(message, ...args) {
this.log('warn', message, ...args);
}
error(message, ...args) {
this.log('error', message, ...args);
}
getLogs(level = null, limit = 100) {
let filteredLogs = this.logs;
if (level) {
filteredLogs = this.logs.filter(log => log.level === level);
}
return filteredLogs.slice(-limit);
}
clearLogs() {
this.logs = [];
return this;
}
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
}
// 使用示例
const config = ConfigManager.getInstance();
const cache = CacheManager.getInstance();
const logger = Logger.getInstance();
// 配置管理
config.watch('config-changed', ({ key, value }) => {
logger.info(`Config changed: ${key} = ${value}`);
});
config.set('theme', 'dark');
console.log('Current theme:', config.get('theme'));
// 缓存管理
cache.set('user:123', { name: 'John', email: 'john@example.com' }, 60000);
console.log('Cached user:', cache.get('user:123'));
// 日志记录
logger.addOutput('localStorage');
logger.info('Application started');
logger.warn('This is a warning message');
logger.error('This is an error message');
console.log('Recent logs:', logger.getLogs('info', 5));单例模式应用场景总结:
💼 最佳实践:单例模式适用于需要全局唯一实例的场景,但要避免过度使用,以免造成代码耦合度过高。在现代JavaScript开发中,ES6模块本身就提供了单例特性。
通过本节JavaScript单例模式完整指南的学习,你已经掌握:
A: 单例模式提供了更好的封装性和控制性,可以延迟初始化、控制访问权限、提供业务方法。全局变量容易被意外修改,缺乏封装性。单例模式是更好的全局访问解决方案。
A: 当对象需要多个实例、需要继承、或者会导致过度耦合时不应使用单例模式。另外,单例模式会增加测试难度,在需要频繁测试的场景中要谨慎使用。
A: 是的,ES6模块在首次导入时会创建实例,后续导入会返回同一个实例,这本身就是单例模式。在现代JavaScript开发中,可以优先考虑使用模块单例。
A: 可以在测试前重置单例实例、使用依赖注入、或者提供测试专用的重置方法。也可以将单例的业务逻辑提取到单独的类中,只对业务逻辑进行测试。
A: JavaScript是单线程的,但在Web Workers环境下需要注意。每个Worker都有独立的全局作用域,单例实例不会共享。如果需要跨Worker共享数据,需要使用其他机制。
// 问题:测试时需要重置单例实例
// 解决:提供重置方法
class TestableSingleton {
constructor() {
if (TestableSingleton.instance) {
return TestableSingleton.instance;
}
this.data = new Map();
TestableSingleton.instance = this;
return this;
}
static getInstance() {
if (!TestableSingleton.instance) {
TestableSingleton.instance = new TestableSingleton();
}
return TestableSingleton.instance;
}
// 测试专用重置方法
static resetInstance() {
TestableSingleton.instance = null;
}
}// 问题:单例模式可能导致内存泄漏
// 解决:提供清理机制
class CleanableSingleton {
constructor() {
if (CleanableSingleton.instance) {
return CleanableSingleton.instance;
}
this.timers = [];
this.listeners = [];
this.resources = new Map();
CleanableSingleton.instance = this;
return this;
}
addTimer(timer) {
this.timers.push(timer);
}
addListener(element, event, handler) {
element.addEventListener(event, handler);
this.listeners.push({ element, event, handler });
}
cleanup() {
// 清理定时器
this.timers.forEach(timer => clearInterval(timer));
this.timers = [];
// 清理事件监听器
this.listeners.forEach(({ element, event, handler }) => {
element.removeEventListener(event, handler);
});
this.listeners = [];
// 清理资源
this.resources.clear();
}
static destroy() {
if (CleanableSingleton.instance) {
CleanableSingleton.instance.cleanup();
CleanableSingleton.instance = null;
}
}
}"掌握单例模式,让你的代码拥有更好的架构设计和资源管理能力。每一个设计模式的应用,都是对代码质量的提升!"