单例模式 - 使用闭包完成单例模式开发

单例模式,也就是要确保在某一个类在全剧中只有一个实例,并且提供一个全局的访问点来访问这一实例。

如何实现一个单例模式,主要讲究两个字,一个字“存”,在类内存在一个属性,如果这个属性不为空那么就初始化当前类赋值给这一属性,另一个字“堵”,将所有的可能再次实例化当前类的方法都“堵”住,并且返回“存”的实例,当然在第一个次实例的时候需要正常实例化,也就是存放实例的属性为空时,可以正常实例化。

在前端开发中可以使用闭包实现单例模式(当然也可以使用 Class 语法糖或者构造函数形式实现),我们将设想一个案例:将一些配置内容或者一些其他内容放在一个单例中供其他内容访问。

首先我们将通过闭包实现一个配置信息的管理,首先我们写一个简单的闭包代码,使 config 属性不受外部污染。

1
2
3
4
5
6
const createConfig = (() {
let config;
return () => {
return config;
}
})()

但是我们要实现一个单例模式,就要在再次返回config前做判断,如果没有 config 要对他进行“实例化“并返回,如果存在 config 就将cunfig返回。

1
2
3
4
5
6
7
8
9
10
11
const createConfig = (() => {
let config;
return () => {
if (!config) {
config = { /* 创建单例对象的代码 */ };
}
return config;
}
})()

const config = createConfig(); // 初始化实例

当然这就完成了一个简单的配置对象的单例,但是我们发现现在的 createConfig 和直接返回一个对象没有什么区别,这是为什么?

有了配置对象的单例,不对对象进行操作基本上没什么用,属于”为醋包饺“,不提倡。

接下来我们将要实现的是,为配置对象新增 set 方法,这个方法将传入两个参数,分别是 keyvalue,作用就是将对象中的 key 的值改成 value 传入的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const createConfig = (() => {
let instance;
return () => {
// 如果不存在 instance 就初始化实例 否则返回原有的 instance
if (!instance) {
instance = {
config: {},
set ( key, value ) {
this.config[key] = value;
}
};
}
return instance;
}
})();

// 初始化两次 config 并且打印
const config1 = createConfig();
const config2 = createConfig();
console.log(
'config1:', config1.config,
'config2:', config2.config
);

// 修改 config 观察 config2 信息
config1.set('key', 0);
console.log('config2.config', config2.config);

当然在前端中不可能是一个页面打开一直不关闭,所以我们就要对数据进行缓存,一下案例中使用了 localStorageJSON 对数据进行了处理,只能处理简单数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const createConfig = (() => {
let instance;
return () => {
// 如果不存在 instance 就初始化
if (!instance) {
// 获取 localStorage 信息
const config = JSON.parse(localStorage.getItem('config') ?? '{}')
instance = {
config,
set ( key, value ) {
this.config[key] = value;
localStorage.setItem('config',
JSON.stringify(this.config ?? {})
);
}
};
}
return instance;
}
})();

// 初始化两次并打印
const config1 = createConfig();
const config2 = createConfig();
console.log(
'config1:', config1.config,
'config2:', config2.config
);

// 修改 config 观察 config2 信息
config1.set('key', 0);
console.log('config2.config', config2.config);

todo 使用 ES6 class形式 完成单例模式
todo 使用 构造函数形式 完成单例模式