开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
单例的核心是确保有且仅有一个实例,并提供一个访问它的全局访问点
单例,即单一实例,能保证何时何地访问该实例,永远都是指向同一个对象的实现模式,就是单例模式。
在javaScript语言中,每一个全局对象,从定义上来看都是一个单例;但我们不可能把所有对象都挂到全局对象下,这个时候,我们就需要通过其他方式实现单例模式并提供唯一访问入口。接着来看看javaScript下可以如何实现单例模式:
- 闭包
先看代码:
const instanceInit = (function () {
let instance;
return function () {
if (!instance) {
instance = {
name: 'instance',
title: '我是一个单例',
};
}
return instance;
};
})();
const instance1 = instanceInit();
const instance2 = instanceInit();
console.log(instance1 === instance2); // true
我们通过闭包的形式,将instance
当做局部变量保存在创建instanceInit
的自执行匿名函数内,并将访问函数以返回值的方式赋值给instanceInit
本身,于是我们可以通过调用它访问instance
实例。闭包的存在,保证了我们的每次访问都能指向该变量。
- 类
class InstanceMode {
static Instance = null;
static getInstance() {
if (InstanceMode.Instance) {
return InstanceMode.Instance;
}
InstanceMode.Instance = new InstanceMode();
return InstanceMode.Instance;
}
constructor() {
this.name = 'instance';
this.title = '我是一个单例';
}
}
const instance1 = InstanceMode.getInstance();
const instance2 = InstanceMode.getInstance();
console.log(instance1 === instance2);
用类实现创建单例,简单来说就是利用类的静态属性,将类实例的创建与引用挂载到它的静态属性下,并提供一个获取方法,使用者通过调用该静态方法,完成实例的创建与获取,并保证后续的引用都指向该静态实例属性。
所以到这里,我们知道,单例的核心实现,是一次创建,后续访问始终指向该创建后的对象
// 核心实现
{
if(instance) {
return instance
}
instance = new CreateInstance()
return instance
}
- 惰性单例
这里再补充一种单例创建模式,惰性单例,即在需要的时候才创建对象实例,在上面的闭包函数和类中,我们都是将单例的创建和返回写到了一个函数内,那么,惰性单例下,就是将创建实例和管理单例分开维护。
function getSingle(fn) {
let result;
return function () {
return result || (result = fn.apply(this, auguments));
};
}
const createSingler = getSingle(createSingle); // createSingle创建对象
由于分开维护,那么我们可以很简单的复用getSingle
函数给我们想要的对象创建函数实现各自的单例模式。
思考:既然有全局对象,为什么我们还需要用单例模式复刻一个闭包下的'全局对象'呢
全局对象容易造成命名空间污染,项目体量大了,很容易覆盖变量,导致难以追溯和定位的bug,减少全局变量的使用是有必要的
当你需要创建的变量需要保证二次访问指向同一个实例,那么就有单例模式的使用价值了。