单例模式
定义
单例模式 (Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点。也就是说,第二次使用同一个类创建新对象的时候,应该得到与第一次创建的对象完全相同的对象。

- 重点:
- 单一实例:确保只创建一个类的实例。
- 全局访问点:提供一个全局可访问的接口用于访问该实例。
- 自我管理:单例类通常自己负责管理它的创建和生命周期
在编程中也有很多对象只需要唯一一个,比如数据库连接、线程池、配置文件缓存、浏览器中的 window/document 等,如果创建多个实例,会带来资源耗费严重,或访问行为不一致等情况。
使用场景
当需要确保在整个应用中只有一个实例存在,并且该实例需要被全局访问时,单例模式是一个不错的选择。然而,需要谨慎使用单例模式,因为它可能会引入全局状态,增加代码的复杂性和耦合度。
React中的状态管理
在 React 应用中,使用单例模式的一个常见场景是状态管理。通常情况下,你可以使用库如 Redux 或 MobX 来管理应用的状态,它们本质上也是单例模式的实现。
- Redux 的 Store:Redux 中的 Store 对象是一个单例,它包含了整个应用的状态树,并且在整个应用中是唯一的。通过 Redux 提供的 API,可以对这个全局状态进行操作和访问。
- MobX 的 Store:类似地,MobX 也可以创建一个单例的状态存储对象,用于管理应用的状态,并确保在整个应用中是唯一的。
这些状态管理工具利用了单例模式的思想,帮助我们在 React 应用中更好地管理和共享状态。当需要在 React 应用中跨组件共享状态、统一管理状态更新时,可以考虑使用这些状态管理库,并将其视为单例对象来管理应用的状态。
Vuex
Vuex 是 Vue 的官方状态管理库,它实现了一个集中式存储,用来存储所有组件的共享状态。这个存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若状态发生变化,相应的组件也会得到高效的更新。Vuex 的 store 是一个单例,整个应用只创建一个 store 实例,确保了应用各个部分状态的一致性。
全局Loading
ElementUI 中的全屏 Loading 蒙层调用有两种形式:
- 指令形式:Vue.use(Loading.directive)
- 服务形式:Vue.prototype.$loading = service
用服务方式使用全屏 Loading 是单例的,即在前一个全屏 Loading 关闭前再次调用全屏 Loading,并不会创建一个新的 Loading 实例,而是返回现有全屏 Loading 的实例。
全局方法/常量
全局方法: 通过使用单例模式,你可以创建一个包含全局方法的对象,并确保在整个应用程序中只存在一个该对象的实例。这样可以保证全局方法在整个应用程序中的唯一性,类似于单例模式的概念。
全局常量: 同样地,通过使用单例模式,你可以创建一个包含全局常量的对象,并确保在整个应用程序中只存在一个该对象的实例。这样可以保证全局常量在整个应用程序中的唯一性,也类似于单例模式的概念。
虽然全局方法和常量本身并不是单例模式,但通过利用单例模式的思想来确保它们的唯一性,可以帮助你更好地管理和共享全局方法和常量,从而避免重复定义和管理的问题。
优缺点
优点
- 节约开支,提高性能: 单例模式在创建后在内存中只存在一个实例,节约了内存开支和实例化时的性能开支,特别是需要重复使用一个创建开销比较大的类时,比起实例不断地销毁和重新实例化,单例能节约更多资源,比如数据库连接
- 解决资源多重占用: 单例模式可以解决对资源的多重占用,比如写文件操作时,因为只有一个实例,可以避免对一个文件进行同时操作
- 提高系统流畅度: 只使用一个实例,也可以减小垃圾回收机制 GC(Garbage Collecation) 的压力,表现在浏览器中就是系统卡顿减少,操作更流畅,CPU 资源占用更少
缺点
- 单例模式本质上提供了一个全局状态,这可能导致代码难以测试和维护,因为系统的不同部分可能会以不可预见的方式相互影响
- 对扩展不友好:一般不容易扩展,因为单例模式一般自行实例化,没有接口
- 与单一职责原则冲突:一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
实现
构造函数
let singleDemo;
function SingleDemo() {
if (!singleDemo) {
singleDemo = this;
}
return singleDemo;
}
SingleDemo.prototype.show = function () {
console.log("我是单例模式");
};
const single1 = new SingleDemo();
const single2 = new SingleDemo();
console.log(single1 === single2);
闭包
var Singletion = (function () {
var instance
function User(name, age) {
this.name = name
this.age = age
}
return function (name, age) {
if (!instance) {
instance = new User(name, age)
}
return instance
}
})()
console.log(Singletion() === Singletion()) // true
类
class Singleton {
constructor(name, age) {
if (!Singleton.instance) {
this.name = name
this.age = age
Singleton.instance = this
}
return Singleton.instance
}
}
console.log(new Singleton('张三', 18) === new Singleton('李四', 20)) // true