我正在参与掘金创作者训练营第6期,点击了解活动详情
前言
单例模式属于创建类型的一种常用的设计模式,通过单例模式的方法创建的类在当前进程中只有一个实例。两个重点:
- 1、单例类只能有一个实例。
- 2、单例类必须给所有其他对象提供这一实例。
应用场景
当我们希望一个类只能存在唯一的对象实例时就应该想到应用单例模式,比如购物车、登陆框、全局消息提示弹窗、全局状态管理器如:(redux 和 vuex 的 store)等等。
常规方案
闭包版本
原理:利用闭包在函数运行结束后,依然可以保存函数作用域中变量的特性,通过闭包函数检查该对象是否被创建,如果被创建就返回该对象,否则就先创建该对象再返回,这样就实现了一个类只能创建一个对象。
function Storage(){
return {
data:{},
get(key){
return this.data[key]
},
set(key,value){
return this.data[key] = value
},
}
}
function singleton(classObj) {
let instance = null
return function () {
if (!instance) {
instance = new classObj()
}
return instance
}
}
const singletonStorage= singleton(Storage);
验证
const instance1=singletonStorage()
const instance2=singletonStorage()
instance2===instance1 //true
class版本
原理:添加getInstance方法获取实例,getInstance中会判断类是都挂在了实例,若是就直接返回已创建的实例,若否创建实例并返回和保存实例,这样使用时调用getInstance方法实现了一个类只能创建一个对象,当然由于当前constructor不支持私有修饰符,该方式实现的单例不够严谨通过 new singletonStorage 方式是可以重复创建多个实例的。
class singletonStorage {
data={}
// 获取方法
get(key){
return this.data[key]
}
// 存储方法
set(key,value){
return this.data[key] = value
}
// 外部调用此函数实例化
static getInstance() {
if (!singletonStorage.instance) {
singletonStorage.instance = new singletonStorage
}
return singletonStorage.instance
}
}
验证
const instance1=singletonStorage.getInstance()
const instance2=singletonStorage.getInstance()
instance2===instance1 //true
ES6模块实现
直接上代码
//Storage.js
let Storage={
data:{},
get(key){
return this.data[key]
},
set(key,value){
return this.data[key] = value
},
}
export default Storage
验证
//a.js
export Storage from 'Storage.js'
//b.js
export Storage from 'Storage.js'
//c.js
import instance1 from 'a.js'
import instance2 from 'b.js'
instance2===instance1 //true
不知道你被惊艳到了没,使用ES6模块是没有经过任何特别处理,就实现了单例模式,是的!其实ES6模块天然就是单例
重温ES6--模块原理
引用阮一峰老师《ECMAScript 6入门》中的表述:
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。相比而言CommonJS 和 AMD 模块,都只能在运行时确定这些东西。 如果多次重复执行同一句
import语句,那么只会执行一次,而不会执行多次。
ES6 模块与 CommonJS 模块的差异包括如下两点:
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
结论
通过以上信息,相信聪明如你,一定get到了重点:
- ES6模块
import语句时在编译阶段执行 - 重复执行同一句
import语句,只会执行一次 - ES6 模块输出的是值的引用
以上几点决定了ES6模块天然就支持单例,另一方面缺了其中一点也就不支持单例了!
在应用场景中如果需要使用单例模式,可以考虑使用ES6模块,无缝支持单例
好的就到这里,欢迎评论交流指正!