一 解释什么是单例模式以及如何在JavaScript中实现它?
1.1 是什么
单例模式(Singleton Pattern):创建型模式,提供了⼀种创建对象的最佳⽅式,这种模式涉及到⼀个单⼀的类,该类负责创建⾃⼰的对象,同时确保只有单个对象被创建
在应⽤程序运⾏期间,单例模式只会在全局作⽤域下创建⼀次实例对象,让所有需要调⽤的地⽅都共享这⼀单例对象,如下图所⽰:

从定义上来看,全局变量好像就是单例模式,但是⼀般情况我们不认为全局变量是⼀个单例模式,原因是:
1.2实现
在 javascript 中,实现⼀个单例模式可以⽤⼀个变量来标志当前的类已经创建过对象,如果下次获取当前类的实例时,直接返回之前创建的对象即可,如下:
// 定义⼀个类
function Singleton(name) {
this.name = name;
this.instance = null;
}
// 原型扩展类的⼀个⽅法getName()
Singleton.prototype.getName = function() {
console.log(this.name)
};
// 获取类的实例
Singleton.getInstance = function(name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance
};
// 获取对象1
const a = Singleton.getInstance('a');
// 获取对象2
const b = Singleton.getInstance('b');
// 进⾏⽐较
console.log(a === b);
1.2.1 立即执行函数表达式 (IIFE) + 闭包
使用立即执行函数表达式可以创建一个私有作用域,通过闭包来保存单例实例,确保外部只能通过暴露的方法访问这个实例。
var Singleton = (function () {
var instance;
function createInstance() {
// 单例的实际构造逻辑
var obj = new Object("I am the instance");
return obj;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// 使用单例
var singleton1 = Singleton.getInstance();
var singleton2 = Singleton.getInstance();
console.log(singleton1 === singleton2); // 输出 true,证明是同一个实例
1.2.2 ES6 Class + 私有静态属性
在ES6及更高版本的JavaScript中,可以通过私有静态属性来实现单例模式。这种方法更符合现代JavaScript的编码风格。
class Singleton {
static instance = null;
constructor(name) {
if (Singleton.instance) {
return Singleton.instance;
}
this.name = name;
Singleton.instance = this;
}
}
// 使用单例
const singleton1 = new Singleton("Instance 1");
const singleton2 = new Singleton("Instance 2");
console.log(singleton1 === singleton2); // 输出 true,证明是同一个实例
在这两种实现中,关键点都是确保无论实例化多少次,返回的都是同一个对象实例。第一种方法通过闭包和惰性初始化实现,而第二种方法利用了ES6的静态属性来存储实例。
1.3 使用场景
在前端中,很多情况都是⽤到单例模式,例如⻚⾯存在⼀个模态框的时候,只有⽤⼾点击的时候才会创建,⽽不是加载完成之后再创建弹窗和隐藏,并且保证弹窗全局只有⼀个
可以先创建⼀个通常的获取对象的⽅法,如下
const getSingle = function(fn) {
let result;
return function() {
return result || (result = fn.apply(this, arguments));
}
};
创建弹窗的代码如下
const createLoginLayer = function() {
var div = document.createElement('div');
div.innerHTML = '我是浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
};
const createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById('loginBtn').onclick = function() {
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};
上述这种实现称为惰性单例,意图解决需要时才创建类实例对象
并且 Vuex 、 redux 全局态管理库也应⽤单例模式的思想,如下图:

现在很多第三⽅库都是单例模式,多次引⽤只会使⽤同⼀个对象,如 jquery、lodash、moment