单例模式 ,保证一个类只有一个实例,并提供一个访问它的全局访问点。也就是说,第二次使用同一个类创建新对象的时候,应该得到与第一次创建的对象完全相同的对象。
浏览器中的 window 和 document 全局变量,这两个对象都是单例,任何时候访问他们都是一样的对象,
window表示包含 DOM 文档的窗口,document是窗口中载入的 DOM 文档,分别提供了各自相关的方法
在 ES6 新增语法的 Module 模块特性,通过
import/export导出模块中的变量是单例的,也就是说,如果在某个地方改变了模块内部变量的值,别的地方再引用的这个值是改变之后的。项目中的全局状态管理模式 Vuex、Redux、MobX 等维护的全局状态,vue-router、react-router 等维护的路由实例
- localStorage的封装 ;
- 全局静态模态框
如何对构造函数使用 new 操作符创建多个对象时,仅获取同一个单例对象呢?。
单例模式的通用实现:
- Singleton :特定类,这是我们需要访问的类,访问者要拿到的是它的实例;
- instance :单例,是特定类的实例,特定类一般会提供
getInstance方法来获取该单例; - getInstance :获取单例的方法,或者直接由
new操作符获取;
有几个实现点要关注一下:
- 访问时始终返回的是同一个实例;
- 自行实例化,无论是一开始加载的时候就创建好,还是在第一次被访问时;
- 一般还会提供一个
getInstance方法用来获取它的实例;

IIFE 方式创建单例模式
使用立即调用函数 IIFE 将不希望公开的单例实例 instance 隐藏。
const Singleton = (function() {let _instance = null // 存储单例const Singleton = function() {if (_instance) return _instance // 判断是否已有单例_instance = thisthis.init() // 初始化操作return _instance}Singleton.prototype.init = function() {this.foo = 'Singleton Pattern'}Singleton.getInstance = function() {if (_instance) return _instance_instance = new Singleton()return _instance}return Singleton})()const visitor1 = new Singleton()const visitor2 = new Singleton() // 既可以 new 获取单例const visitor3 = Singleton.getInstance() // 也可以 getInstance 获取单例console.log(v1 === v2, v1 === v3, v2 === v3); // true true true
单例模式赋能
将单例模式的创建逻辑和特定类的功能逻辑拆开,这样功能逻辑就可以和正常的类一样。
/* 功能类 */class FuncClass {constructor(bar) {this.bar = barthis.init()}init() {this.foo = 'Singleton Pattern'}}/* 单例模式的赋能类 */const Singleton = (function() {let _instance = null // 存储单例const ProxySingleton = function(bar) {if (_instance) return _instance // 判断是否已有单例_instance = new FuncClass(bar)return _instance}ProxySingleton.getInstance = function(bar) {if (_instance) return _instance_instance = new Singleton(bar)return _instance}return ProxySingleton})()const visitor1 = new Singleton('单例1')const visitor2 = new Singleton('单例2')const visitor3 = Singleton.getInstance()console.log(visitor1 === visitor2) // trueconsole.log(visitor1 === visitor3) // true
懒汉式-饿汉式
有时候一个实例化过程比较耗费性能的类,但是却一直用不到,如果一开始就对这个类进行实例化就显得有些浪费,那么这时我们就可以使用惰性创建(懒汉式)。
之前的例子都属于惰性单例,实例的创建都是 **new** 的时候才进行。
懒汉式 <——**> **饿汉式:
- 懒汉式单例是在使用时才实例化
饿汉式是当程序启动时或单例模式类一加载的时候就被创建。
class FuncClass {constructor() { this.bar = 'bar' }}// 饿汉式const HungrySingleton = (function() {const _instance = new FuncClass()return function() {return _instance}})()// 懒汉式const LazySingleton = (function() {let _instance = nullreturn function() {return _instance || (_instance = new FuncClass())}})()const visitor1 = new HungrySingleton()const visitor2 = new HungrySingleton()const visitor3 = new LazySingleton()const visitor4 = new LazySingleton()console.log(visitor1 === visitor2) // trueconsole.log(visitor3 === visitor4) // true
饿汉式在 HungrySingleton 这个 IIFE 执行的时候就进入到 FuncClass 的实例化流程了,
懒汉式的 LazySingleton 中 FuncClass 的实例化过程是在第一次 new 的时候才进行的。
单例模式的优缺点
单例模式主要解决的问题就是节约资源,保持访问一致性。
优点:
- 单例模式在创建后在内存中只存在一个实例,节约了内存开支和实例化时的性能开支,特别是需要重复使用一个创建开销比较大的类时,比起实例不断地销毁和重新实例化,单例能节约更多资源,比如数据库连接;
- 只使用一个实例,也可以减小垃圾回收机制 GC(Garbage Collecation) 的压力,表现在浏览器中就是系统卡顿减少,操作更流畅,CPU 资源占用更少;
缺点:
- 单例模式对扩展不友好,一般不容易扩展,因为单例模式一般自行实例化,没有接口;
