单例模式
单例模式又称为单体模式,保证一个类只有一个实例,并提供一个访问它的全局访问点。也就是说,第二次使用同一个类创建新对象的时候,应该得到与第一次创建的对象完全相同的对象。
现实生活中的存档、编程中的数据库连接、线程池、配置文件缓存、浏览器的window/document等,如果创建多个实例,会带来资源耗费严重,或访问行为不一致的情况。
在类似场景中,这些例子有以下特点:
- 每次访问者来访问,返回的都是同一个实例。
- 如果一开始实例没有创建,那么这个特定类需要自行创建这个实例。
单例模式的实现
常见的单例(而非单例模式的应用):
- 浏览器中的 window 和 document 全局变量,这两个对象都是单例,任何时候访问他们都是一样的对象,window 表示包含 DOM 文档的窗口,document 是窗口中载入的 DOM 文档,分别提供了各自相关的方法。
- 导出模块中的变量是单例的,也就是说,如果在某个地方改变了模块内部变量的值,别的地方再引用的这个值是改变之后的。
- 项目中的全局状态管理模式 Vuex 维护的全局状态,维护的路由实例,在单页应用的单页面中都属于单例的应用(但不属于单例模式的应用)。
当创建了一个类后,可以通过new关键字调用构造函数进而生成任意多的实例对象,那如何对构造函数使用new关键字创建多个对象时,仅获取同一个实例对象呢?
主要有以下几个概念:
- Singleton:特定类,这是我们需要访问的类,访问者要拿到它的实例。
- instance:单例,是特定类的实例,特定类一般会提供getInstence方法来获取该单例。
- getInstance:获取单例的方法,或者直接由new操作符获取。
在实现单例模式的时候,需要注意以下几点:
- 访问时始终返回的是同一个实例;
- 自行实例化,无论是一开始加载的时候就创建好,还是在第一次被访问时;
- 一般还会提供一个getInstance方法来获取它的实例。
具体实现:
1 | class SingleCase { |
还可以使用闭包实现:
1 | SingleCase.getInstance = (function(){ |
单例模式的优缺点:
优点:
- 节约开支,提高性能:单例模式在创建后在内存中只存在一个实例,节约了内存开支和实例化时的性能开支,特别是重复使用一个创建开销比较大的类时,比起实例不断销毁和重新实例化,单例能节约更多的资源,
- 解决资源多重占用:单例模式可以解决对资源的多重占用,比如写文件操作时,因为只有一个实例,可以避免对一个文件同时进行操作。
- 提高系统流畅度:只使用一个实例,也可以减少立即回收机制的压力,表现在浏览器中就是系统卡顿减少,操作更流畅,CPU占用资源更少。
缺点:
- 对扩展不友好:一般不容易扩展,因为单例模式一般自行实例化,没有接口。
- 与单一职责原则冲突:一个类只关心内部逻辑,而不关心外面怎样来实例化。
使用场景
- 当一个类的实例化过程消耗的资源过多,可以使用单例模式来避免性能浪费;
- 当项目中需要一个公共的状态,那么需要使用单例模式来保证访问一致性。