本文原载于 SegmentFault 社区专栏:前端学习
单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个访问它的全局访问点
单例模式是设计模式中较为简单,好理解的一种模式,但是它的使用场景是很广泛的,包括大名鼎鼎的 Vuex 也使用了单例模式。
本文会介绍单例模式的两种实现方法:
类和闭包,同时也会对 Vuex 中的单例模式进行介绍。
类 Class 是 ES6 新增的语法,在之前我们想要新建一个对象实例,是通过 new 构造函数的方式来实现的。
我们每次调用 new 的时候,都会生成一个新的实例对象,每个实例对象之间是完全独立的。
function Car (name) {
this.name = name;
}
var car1 = new Car('benz');
var car2 = new Car('audi');
console.log(car1 === car2)
那么我们来思考一下,怎么才能每次 new 都返回同一个实例对象呢?
肯定是有一个变量将第一次 new 生成的实例对象保存了下来,后面再执行 new 的时候,就直接返回第一次生成的实例对象,这样就实现了单例。
类实现
class SingletonCar {
constructor () {
this.name = 'benz';
}
static getInstance () {
if (!SingletonCar.instance) {
SingletonCar.instance = new SingletonCar();
}
return SingletonCar.instance;
}
}
let car1 = SingletonCar.getInstance();
let car2 = SingletonCar.getInstance();
console.log(car1 === car2)
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
静态方法可以直接在父类上调用 SingletonCar.getInstance(),而不是在实例对象上调用。
如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
用类来实现单例模式,只要记住这个 getInstance() 静态方法就可以了。
闭包实现
var SingletonCar = (function () {
var instance;
var SingletonCarTemp = function () {
this.name = 'benz';
};
return function () {
if (!instance) {
instance = new SingletonCarTemp();
}
return instance;
}
})();
var car1 = new SingletonCar();
var car2 = new SingletonCar();
console.log(car1 === car2)
借助闭包,在内存中保留了 instance 变量,不会被垃圾回收,用来保存唯一的实例,多次调用 new 的时候,只返回第一次创建的实例。
Vuex 是 Vue 的状态管理类库,类似于 Redux 之于 React,其实他们的理念都是来自于 Flux 架构,用一个全局的 Store 存储应用所有的状态,然后提供一些 API 供用户去读取和修改。
一看到全局唯一的 Store,就可以想到是单例模式了。
如何引用 Vuex
import Vue from 'vue'
import Vuex form 'vuex'
import store from './store'
Vue.use(Vuex)
new Vue({
el: '#app',
store
})
Vuex 是一个插件,通过调用 Vue.use() 方法可以安装这个插件,具体 Vue 插件的写法可以参考官方文档
Vuex 在内部实现了一个 install 方法,该方法会在 Vue.use() 时被调用,Vue 会被作为参数传入 install 方法中,从而把 Store 注入到 Vue 中。
但是如果你试过在开发环境多次调用 Vue.use(Vuex) 的话,就会知道是浏览器是会报错的,接下来我们看一下 Vuex 的内部实现。
Vuex 如何保证唯一 Store
let Vue
...
export function install (_Vue) {
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 = _Vue
applyMixin(Vue)
}
完整源码路径在 vuex/src/store.js
我们可以看到,在 Vuex 内部,先定义了一个变量 Vue,注意这里不是真正的 Vue,只是一个变量,也叫 Vue。
在 Vue.use(Vuex) 的时候,会调用 install 方法,真正的 Vue 会被当做参数传入,如果多次执行 Vue.use(Vuex),也只会生效一次,也就是只会执行一次 applyMixin(Vue),所以只会有一份唯一的 Store,这就是 Vuex 中单例模式的实现。
思路
我们在业务开发的过程中,有很多需求都会有 Loading 状态,这时候直接掏出单例模式,记住上面的 getInstance 静态方法或者闭包 instance 变量,三下五除二即可实现。
完整代码
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>单例模式全局Loadingtitle>
<style>
.loading {
width: 100vw