【公众号:CS阿吉】
单例模式属于「创建型设计模式」。
创建型设计模式是一类处理对象创建的设计模式,通过某种方式控制对象的创建来避免基本对象创建时可能导致设计上的问题或增加设计上的复杂度。
1. 定义
单例模式:一个类只有一个实例,且可被全局访问。
2. 背景
在系统的某些场景中,只需要一个实例,多个类的实例会造成混乱。比如,实验室的打印机有多个打印任务,但只能有一个任务正在打印,此时需要一个打印机类的实例来控制,多个实例相当于多个任务在打印,而打印机并不支持同时打印多个任务。
如何确保 一个类只有一个实例 (一个打印机一段时间内只能打印一个任务)呢?类保存其生成的唯一实例(在JS中就是闭包。若不理解,可继续读文看代码演示)。
思考:为什么不通过定义一个全局变量呢?根据单例模式的定义,全局变量可满足被全局访问,但无法防止实例化多个对象,与一个类只有一个实例的理念冲突。
3. UML类图
4. 通用写法
下面有两种书写方式避免了内存浪费问题,单例对象在被使用时才会初始化,也被称为「惰性单例」。
4-1. ES5实现
var Singleton = function(name){
this.name = name;
};
Singleton.instance = null;
Singleton.getInstance = function(name) {
if(!this.instance) this.instance = new Singleton(name);
return this.instance;
}
// 验证:一个类只有一个实例
var a = Singleton.getInstance('a'); // Singleton {name: "a"}
var b = Singleton.getInstance('b'); // Singleton {name: "a"}
console.log(a === b); // true
4-2. ES6实现
class Singleton {
constructor(name){
this.name = name;
this.instance = null;
}
static getInstance(name) {
if(!this.instance) this.instance = new Singleton(name);
return this.instance
}
}
// 验证:一个类只有一个实例
let a = Singleton.getInstance('a'); // Singleton {name: "a"}
let b = Singleton.getInstance('b'); // Singleton {name: "a"}
console.log(a === b); // true
5. 优点
(1)只有一个实例,减少内存开销。
(2)设置全局访问点,优化和共享资源的访问。
6. 缺点
(1)扩展困难,违背开闭原则。
(2)职责过重,违背单一职责原则。
(3)可能导致内存溢出。
(4)JS的自动垃圾回收,对于长时间未使用的实例化的对象进行回收,会导致对象状态的丢失。
7. 应用
7-1. VueX
VueX 用一个全局的 Store 存储应用所有的状态。
let Vue // bind on install
export function install (_Vue) {
// 避免重复安装
if (Vue && _Vue === Vue) {
if (__DEV__) {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
// 保存Vue
Vue = _Vue
// 只进行一次初始化,实例化全局对象
applyMixin(Vue)
}
7-2. Redux
Redux 用一个全局的 Store 存储应用所有的状态。通过createStore()函数创建一个Store。
createStore()创建出来的对象,提供了与state相关的4个API:(1)getState()返回当前的state树(2)dispatch()派发action,改变state的唯一途径(3)subscribe()添加监听函数,每次dispatch时调用(4)replaceReducer()替换当前用于计算的reducer
function createStore(reducer, preloadedState, enhancer) {
let currentState = preloadedState // 初始状态
let isDispatching = false // 是否正在dispatch 的标志位
// 返回当前state
function getState() {
if (isDispatching) {
throw new Error()
}
return currentState
}
// 订阅 store变化
function subscribe(listener) {}
// 派发action,更改state
function dispatch(action) {}
// 动态修改reducer
function replaceReducer(nextReducer) {}
// 保证 nextListeners 和 currentListeners 不会指向同个数组
function ensureCanMutateNextListeners() {}
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
}
}
之前的一篇文章:redux源码阅读[1]
References
[1]
redux源码阅读: https://juejin.cn/post/6844904160069058567#heading-3