代码实现
以数据库连接为例简单实现一个单例
class Database {
constructor(options) {
this.options = options
}
getConnection() {
console.log('You are now connected to the database.')
}
}
const DatabaseSingleton = (function () {
let instance = null
return function (options) {
if (instance === null) {
instance = new Database(options)
}
return instance
}
})()
const db1 = new DatabaseSingleton({ host: 'localhost', port: 1111 })
const db2 = new DatabaseSingleton({ host: 'localhost', port: 2222 })
console.log(db1 === db2) // true
通用实现
通用的单例模式一般由以下几个部分构成
- Singleton :特定类,这是我们需要访问的类,访问者要拿到的是它的实例;
- instance :单例,是特定类的实例,特定类一般会提供
getInstance
方法来获取该单例; - getInstance :获取单例的方法,或者直接由
new
操作符获取;
class Singleton {
static instance = null
static getInstance(options) {
if (Singleton.instance === null) {
return new Singleton(options)
}
return Singleton.instance
}
constructor(options) {
if (Singleton.instance === null) {
this.options = options
Singleton.instance = this
}
return Singleton.instance
}
}
const s1 = new Singleton('s1')
const s2 = Singleton.getInstance('s2')
console.log(s1 === s2) // true
懒汉式 vs 饿汉式
懒汉式和饿汉式的区别就在于单例 instance 是否是懒加载的
懒汉式
function Money(currency, amount) {
this.currency = currency
this.amount = amount
}
Money.prototype.getMoney = function () {
return `${this.currency} ${this.amount}`
}
const MoneySingletonLazy = (function () {
let instance = null
return function (currency, amount) {
if (instance === null) {
instance = new Money(currency, amount) // 懒加载
}
return instance
}
})()
const m1 = MoneySingletonLazy('$', 100)
const m2 = MoneySingletonLazy('$', 100)
console.log(m1 === m2)
饿汉式
function Money(currency, amount) {
this.currency = currency
this.amount = amount
}
Money.prototype.getMoney = function () {
return `${this.currency} ${this.amount}`
}
const MoneySingletonHungry = (function (currency, amount) {
const instance = new Money(currency, amount) // 直接加载
return function () {
return instance
}
})('$', 100)
const m1 = MoneySingletonHungry()
const m2 = MoneySingletonHungry()
console.log(m1 === m2)
优缺点
优点
- 单例模式在创建后在内存中只存在一个实例,节约了内存开支和实例化时的性能开支,特别是需要重复使用一个创建开销比较大的类时,比起实例不断地销毁和重新实例化,单例能节约更多资源,比如数据库连接;
- 单例模式可以解决对资源的多重占用,比如写文件操作时,因为只有一个实例,可以避免对一个文件进行同时操作;
- 只使用一个实例,也可以减小垃圾回收机制
GC(Garbage Collecation)
的压力,表现在浏览器中就是系统卡顿减少,操作更流畅,CPU 资源占用更少;
缺点
- 单例模式对扩展不友好,一般不容易扩展,因为单例模式一般自行实例化,没有接口;
- 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化;
使用场景
- 当一个类的实例化过程消耗的资源过多,可以使用单例模式来避免性能浪费;
- 当项目中需要一个公共的状态,那么需要使用单例模式来保证访问一致性;