单例模式 (Singleton Pattern)又称为单体模式,保证一个类只有一个实例,并提供一个访问它的全局访问点。也就是说,每次使用同一个类创建的新对象都是完全相同的。
单例模式是设计模式中相对较为容易理解、容易上手的一种模式,同时因为其具有广泛的应用场景,也是面试题里的常客
什么场景需要单例模式呢?
- 一些游戏,如:植物大战僵尸,我们肯定希望每次玩的时候都是从之前的存档继续,假如,第二天来玩是从第一关卡开始,那我直接把电脑扔到了窗外。而有了存档,那每次玩都在相同的存档处继续,这就用到了单例。
- 网站的登录框、购物车等
- redux、vuex等状态管理
单例模式的实现
我们暂且不讨论如何实现单例模式,先思考下如何保证一个类仅有一个实例呢?
一般情况下,我们创建了一个类(本质是构造函数)后,会通过new关键字调用构造函数从而生成实例对象。就像这样:
class Man {
callWife() {
console.log('已经给您的老婆小丽打电话');
}
}
const man1 = new Man();
const man2 = new Man();
console.log(man1 === man2); // false
上面我们先new了一个man1,又new了一个man2,很明显man1和man2是相互独立的对象,各占一块内存空间。而单例模式的效果是:不管我们创建多少次对象,只返回给我们第一次创建的那个唯一实例。
要实现这一点,就需要构造函数具备判断自己是否已经创建过一个实例的能力。我们把这段逻辑写成一个静态方法:
class Man {
callWife() {
console.log('已经给您的老婆小丽打电话');
}
static getInstance() {
if(!Man.instance) {
Man.instance = new Man();
}
return Man.instance;
}
}
const man1 = Man.getInstance();
const man2 = Man.getInstance();
console.log(man1 === man2); // true
除了这种方式之外,getInstance方法的逻辑还可以用闭包来实现:
Man.getInstance = (function() {
let instance = null;
return function() {
if(!instance) {
instance = new Man();
}
return instance;
}
})();
现在,在getInstance方法的判断下,不管调用多少次,Man都只会返回给我们同一个实例,man1和man2现在都指向这个唯一的实例。
Vuex中的单例模式
Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。 ——Vuex官方文档
在Vue中,组件间通信最常用的是props(仅限于父子组件通信),复杂一点的(比如兄弟组件间通信)可以通过父组件来间接搞定。
但当组件非常多、关系非常复杂时,上面提到的通信方式就变的难以维护。这时最好的做法是将共享的数据抽出来、放在全局,供组件们按照一定的的规则去存取数据,保证状态以一种可预测的方式发生变化。于是便有了 Vuex,这个用来存放共享数据的唯一数据源,就是 Store。关于 Vuex 的细节,大家可以参考Vuex的官方文档。
Vuex如何确保Store的唯一性
先来看下如何在项目中引入Vuex:
// 安装vuex插件
Vue.use(Vuex)
// 将store注入到Vue实例中
new Vue({
el: '#app',
store
})
通过调用Vue.use()方法,安装 Vuex 插件。Vuex 插件是一个对象,它在内部实现了一个 install 方法,这个方法会在插件安装时被调用,从而把 Store 注入到Vue实例里去。
在install方法里,实现的逻辑和getInstance方法很相似:
let Vue // Vue的作用和上面的instance一样
...
export function install (_Vue) {
// 判断传入的Vue实例对象是否已经被install过(是否有了唯一的state)
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
// 若没有,则为这个Vue实例对象install一个唯一的Vuex
Vue = _Vue
// 将Vuex的初始化逻辑写进Vue的钩子函数里
applyMixin(Vue)
}
上面便是 Vuex 源码中单例模式的实现办法了,套路可以说和getInstance如出一辙。通过这种方式,可以保证一个 Vue 实例(即一个 Vue 应用)只会被 install 一次 Vuex 插件,所以每个 Vue 实例只会拥有一个全局的 Store。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。