单例模式
介绍
单例模式,即对一个 class 只能创建一个实例,即便调用多次。
示例
如一个系统的登录框、遮罩层,可能会被很多地方调用,但登录框只初始化一次即可,以后的直接复用。
再例如,想 Vuex Redux 这些全局数据存储,全局只能有一个实例,如果有多个,会出错的。
伪代码
登录框,初始化多次没必要。
js
class LoginModal { }
// modal1 和 modal2 功能一样,没必要初始化两次
const modal1 = new LoginModal()
const modal2 = new LoginModal()
全局存储,初始化多个实例,会出错。
js
class Store { /* get set ... */ }
const store1 = new Store()
store1.set(key, value)
const store2 = new Store()
store2.get(key) // 获取不到
演示
使用 TS 特性
static
静态属性和方法 —— 详细介绍一下,对比“静态xx”和“实例xx”private
外部无法直接初始化
js
class Singleton {
// private - 外部无法初始化
private constructor() { }
// static 属性
private static instance: Singleton | null
// static 方法
static getInstance(): Singleton {
// 这里也可以写 `this.instance` ,注意和实例方法中 this 的区别!!!
if (Singleton.instance == null) {
Singleton.instance = new Singleton()
}
return Singleton.instance
}
}
// const s1 = new Singleton() // 直接初始化会报错
// Singleton.instance // 直接访问 instance 也会报错
// 创建实例
const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
console.log(s1 === s2) // true
不使用 TS 特性
最常见的方式,使用闭包
js
function genGetInstance() {
let instance // 闭包
class Singleton {}
return () => {
if (instance == null) {
instance = new Singleton
}
return instance
}
}
const getInstance = genGetInstance()
const s1 = getInstance()
const s2 = getInstance()
结合模块化语法,会更好一些
js
let instance // 闭包
class Singleton {}
// 外部只能 import 这个函数
export default () => {
if (instance == null) {
instance = new Singleton
}
return instance
}
是否符合设计原则?
5 大设计原则中,最重要的就是:开放封闭原则,对扩展开放,对修改封闭
- 内部封装 getInstance ,内聚,解耦
注意事项
JS 是单线程语言,如果是 Java 等多线程语言,单例模式需要加线程锁。
场景
登录框
一个页面有很多地方调用登录框,使用单例模式
ts
class LoginForm {
private state: string = 'hide' // 'hide' / 'show'
private constructor() {}
show() {
if (this.state === 'show') {
console.log('已经显示了')
return
}
console.log('显示 LoginForm')
this.state = 'show'
}
hide() {
if (this.state === 'hide') {
console.log('已经隐藏了')
return
}
console.log('隐藏 LoginForm')
this.state = 'hide'
}
private static instance: LoginForm | null = null
static getInstance(): LoginForm {
// 注意这里的 this
if (this.instance == null) this.instance = new LoginForm()
return this.instance
}
}
const loginForm1 = LoginForm.getInstance()
const loginForm2 = LoginForm.getInstance()
其他
前端用到严格的单例模式并不多,但单例模式的思想到处都有
- 自定义事件 eventBus 全局只有一个
- Vuex Redux store 全局只有一个