单例模式 (Singleton pattern)
单例模式介绍
单例模式是一种创建型设计模式,确保某个类只有一个实例,并提供全局访问点.
这种模式适用于当需要确保系统中的特定类只有一个实例存在,且需要在整个应用中全局访问该实例时.单例模式的核心思想是通过私有化构造函数以及全局访问方法,控制实例的创建和访问.
要实现单例模式,常常需要考虑以下几点:
- 私有化构造函数:通过将类的构造函数设置为私有,可以防止外部直接实例化对象.
- 静态方法获取实例:提供一个静态方法来获取单例对象,该方法负责确保只创建一个实例并返回该实例.
- 懒加载:延迟对象的实例化,只有在首次调用时才创建对象.
- 线程安全:如果在多线程环境下使用单例模式,需要确保线程安全,避免出现多个实例同时创建的问题.
单例模式在许多场景下都有广泛应用,例如数据库连接池、线程池、配置信息对象等.它可以减少系统中的内存占用、节省资源、提高程序性能,并且方便对实例进行管理和维护.
但在实际应用中需要注意线程安全、懒加载等问题,以确保单例模式的有效性.
基础实现
js
class Singleton {
constructor(name) {
this.name = name;
}
}
Singleton.getInstance = (() => {
// 通过自执行函数和闭包实现单例模式
let instance;
return (...args) => {
if (!instance) {
instance = new Singleton(...args); // 缓存
}
return instance;
};
})();
// 约定调用方式如下:
const instance1 = Singleton.getInstance("111");
const instance2 = Singleton.getInstance("222");
console.log(instance1 === instance2, instance1.name);
// 输出:true 111, instance1 和 instance2 是同一个实例
//但是如果这样调用:
const instance3 = new Singleton("333");
console.log(instance1 === instance3, instance3.name);
// 输出:false 333, instance1 和 instance3 不是同一个实例
export default Singleton;
进阶版
解决了 new
调用的问题
js
class Singleton {
constructor(name) {
// 判断是否已经有实例
this.name = name;
if (typeof Singleton.instance === "object") {
// 开发环境下可以打印警告
if (process.env.NODE_ENV === "development") console.warn("已经有实例了");
return Singleton.instance;
}
Singleton.instance = this; // 缓存
return this;
}
static getInstance = (() => {
// 通过自执行函数和闭包实现单例模式
let instance;
return (...args) => {
if (!instance) {
Singleton.instance = new Singleton(...args);
instance = Singleton.instance; // 缓存
}
return instance;
};
})();
}
const instance1 = new Singleton("111");
const instance2 = new Singleton("222");
const instance3 = Singleton.getInstance("333");
console.log(instance1 === instance2, instance1.name);
// 输出:true 111, instance1 和 instance2 是同一个实例
console.log(instance1 === instance3, instance2.name);
// 输出:true 111, instance1 和 instance3 是同一个实例
console.log(instance2 === instance3, instance3.name);
// 输出:true 111, instance2 和 instance3 是同一个实例
export default Singleton;
高级版
在原有基础上不需要单独在 class
中处理 而是通过代理
实现
js
class MyClass {
constructor(name) {
this.name = name;
}
}
const singleton = (className) => {
// 通过闭包实现单例模式
let instance;
// 通过 Proxy 代理构造函数,实现单例模式
const proxy = new Proxy(className, {
construct(target, args) {
// 判断是否已经有实例
if (!instance) {
instance = new target(...args);
} else {
// 开发环境下可以打印警告
process.env.NODE_ENV === "development" && console.warn("已存在实例");
}
return instance;
},
});
// 更改原型指向 使得 通过实例的constructor属性 仍然指向 代理
className.prototype.constructor = proxy;
return proxy;
};
const SingletonMyClass = singleton(MyClass);
const instance1 = new SingletonMyClass("111");
const instance2 = new SingletonMyClass("222");
const instance3 = new instance1.constructor("333");
console.log(instance1 === instance2, instance1.name);
// 输出:true 111, instance1 和 instance2 是同一个实例
console.log(instance1 === instance3, instance3.name);
// 输出:true 111, instance1 和 instance3 是同一个实例
console.log(instance2 === instance3, instance3.name);
// 输出:true 111, instance2 和 instance3 是同一个实例
export default SingletonMyClass;
模块化抽离使用
还可以将 singleton 方法抽离出来 模块化使用
模块化抽离使用 点击展开
模块化
javascript
const singleton = (className) => {
let instance;
const proxy = new Proxy(className, {
construct(target, args) {
if (!instance) {
instance = new target(...args);
} else {
process.env.NODE_ENV === "development" && console.warn("已存在实例");
}
return instance;
},
});
className.prototype.constructor = proxy;
return proxy;
};
export default singleton;
使用
javascript
import singleton from "singleton.js";
class MyClass {
constructor(name) {
this.name = name;
}
}
const SingletonMyClass = singleton(MyClass);
export default SingletonMyClass;