您现在的位置是:网站首页 > 单例模式(Singleton)的多种实现方式文章详情
单例模式(Singleton)的多种实现方式
陈川
【
JavaScript
】
12247人已围观
4778字
单例模式是一种常用的设计模式,确保一个类只有一个实例,并提供全局访问点。在JavaScript中,实现单例的方式灵活多样,可以通过闭包、类、模块化等手段达成目标。
基础实现:静态属性方式
最简单的单例实现是通过类的静态属性存储实例。当首次调用时创建实例并缓存,后续调用直接返回缓存的实例。
class Singleton {
static instance = null;
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
}
log() {
console.log('Singleton instance');
}
}
const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1 === s2); // true
这种方式在ES6类语法中清晰直观,但静态属性属于类而非实例,可能被意外修改。
闭包实现:IIFE方式
利用立即执行函数(IIFE)和闭包特性,可以创建完全私有的单例实例,避免外部访问和修改。
const Singleton = (function() {
let instance;
function createInstance() {
return {
log() {
console.log('Private singleton');
}
};
}
return {
getInstance() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2); // true
这种实现完全隐藏了实例创建细节,通过暴露的getInstance方法控制访问,是经典的实现方式。
模块化实现:ES6模块
现代JavaScript的模块系统天然支持单例模式。模块在首次导入时执行且只执行一次,导出的对象自然成为单例。
// singleton.js
let instance;
export default {
log() {
console.log('Module singleton');
}
};
// main.js
import Singleton from './singleton.js';
import Singleton2 from './singleton.js';
console.log(Singleton === Singleton2); // true
模块化实现最为简洁,但需要构建工具支持。在Node.js环境或Webpack等打包工具中可以直接使用。
惰性单例:按需创建
某些场景下需要延迟实例化,直到真正需要时才创建实例。可以通过代理模式或getter拦截实现。
class HeavyResource {
constructor() {
console.log('Expensive operation');
}
}
const LazySingleton = {
get instance() {
if (!this._instance) {
this._instance = new HeavyResource();
}
return this._instance;
}
};
// 直到访问instance属性时才创建实例
console.log('Before access');
const resource = LazySingleton.instance;
这种方式适合初始化成本高的资源,如数据库连接、大型配置文件加载等场景。
线程安全实现(模拟)
虽然JavaScript是单线程语言,但在某些异步场景下仍需考虑初始化竞态问题。可以通过锁机制模拟线程安全。
class SafeSingleton {
static instance;
static lock = false;
static async getInstance() {
if (this.instance) return this.instance;
while (this.lock) {
await new Promise(resolve => setTimeout(resolve, 10));
}
this.lock = true;
try {
if (!this.instance) {
this.instance = new SafeSingleton();
}
} finally {
this.lock = false;
}
return this.instance;
}
}
// 并发测试
Promise.all([
SafeSingleton.getInstance(),
SafeSingleton.getInstance()
]).then(([a, b]) => {
console.log(a === b); // true
});
单例注册表
当需要管理多个单例时,可以使用注册表模式集中管理。通过key-value映射存储不同类型的单例。
class SingletonRegistry {
static instances = new Map();
static getInstance(key, creator) {
if (!this.instances.has(key)) {
this.instances.set(key, creator());
}
return this.instances.get(key);
}
}
const logger = SingletonRegistry.getInstance('logger', () => ({
log: msg => console.log(msg)
}));
const api = SingletonRegistry.getInstance('api', () => ({
fetch: () => Promise.resolve('data')
}));
破坏单例的防护
某些情况下需要防止单例被意外破坏,可以通过以下方式加固:
- 冻结对象防止修改
const strictSingleton = Object.freeze({
method() {}
});
- 构造函数防重写
class ProtectedSingleton {
constructor() {
if (new.target !== ProtectedSingleton) {
throw new Error('Use getInstance()');
}
}
static getInstance() {
if (!this._instance) {
this._instance = new ProtectedSingleton();
}
return this._instance;
}
}
单例与依赖注入
在现代前端框架中,单例常通过依赖注入容器管理。以下是模拟实现:
class Container {
static bindings = new Map();
static singleton(key, creator) {
let instance;
this.bindings.set(key, () => {
if (!instance) instance = creator();
return instance;
});
}
static resolve(key) {
const factory = this.bindings.get(key);
return factory?.();
}
}
// 注册单例服务
Container.singleton('logger', () => ({
log: console.log
}));
// 获取实例
const logger = Container.resolve('logger');
单例模式的变体
- 多例模式(Multiton):限制实例数量而非单个
class Multiton {
static instances = new Map();
static MAX_INSTANCES = 3;
static getInstance(key) {
if (!this.instances.has(key)) {
if (this.instances.size >= this.MAX_INSTANCES) {
throw new Error('Instance limit reached');
}
this.instances.set(key, new Multiton());
}
return this.instances.get(key);
}
}
- 环境隔离单例:根据不同环境(如开发/生产)返回不同实例
class EnvSingleton {
static instances = {
development: null,
production: null
};
static getInstance(env) {
if (!this.instances[env]) {
this.instances[env] = new EnvSingleton(env);
}
return this.instances[env];
}
constructor(env) {
this.env = env;
}
}