目录
定义
单例模式是一种创建型设计模式,它保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用场景
"单例模式的特点,意图解决:维护一个全局实例对象。"
- 引用第三方库(多次引用只会使用一个库引用,如 jQuery)
- 弹窗(登录框,信息提升框)
- 购物车 (一个用户只有一个购物车)
- 全局态管理 store (Vuex / Redux)
项目中引入第三方库时,重复多次加载库文件时,全局只会实例化一个库对象,如 jQuery
,lodash
,moment
..., 其实它们的实现理念也是单例模式应用的一种:
javascript
// 引入代码库 libs(库别名)
if (window.libs != null) {
// 直接返回
return window.libs;
} else {
// 初始化
window.libs = '...';
}
非单例模式
在实现单例模式前,先看一下正常对象实例化,是否符合单例模式,用构造函数或者 Class(本质上还是构造函数)来实现都可以,我们这里就直接使用构造函数做示例,实现如下代码,可以看出相同的构造函数 Singleton,经过两次创建的对象实例 s1、s2 并不相等,对象指向的内存地址不一致,不是同一个实例,不符合单例模式。
javascript
function Singleton() {}
const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1);
console.log(s2);
console.log(s1 === s2);
// {}
// {}
// false
单例模式实现
既然直接实例化不符合单例模式,那要怎么才能实现单例模式呢?要实现单例模式,需要构造函数具备判断自己是否已经创建过一个实例的能力。
闭包方式实现
不管执行多少次,返回的都是同一个实例,而闭包刚好就能满足我们的需求,创建一个立即执行函数,返回一个函数,而这个函数返回函数的内部变量 instance,也就是我们想要的唯一实例。我们实现了如下代码,进行了两次实例化,生成实例对象 s1、s2,根据输出可以判定 s1 与 s2 相等,即 s1,s2 指向同一块内存地址,是同一个实例,符合单例模式。
javascript
const Singleton = (function () {
// 实例变量
let instance = null;
// 实例的构造函数
function getInstance() {}
return function () {
// 判断是否已经 new 过1个实例
if (!instance) {
// 如果实例不存在,则先new一个实例
instance = new getInstance();
}
// 未来不管执行多少次,都返回这个唯一实例
return instance;
};
})();
const s1 = Singleton();
const s2 = Singleton();
console.log(s1);
console.log(s2);
console.log(s1 === s2);
// getInstance {}
// getInstance {}
// true
静态方法实现
ES6 Class 的静态方法也能实现单例模式,原理是借助于静态属性和静态方法,Class 的本质是一个构造函数,存在 static 修饰符的属性称为静态属性,直接挂载在构造函数上,当前类未被销毁时,静态属性也不会被销毁,具有类似于闭包的缓存作用,可以用来存储实例,实现了如下代码,借助 Singleton.getInstance() 生成实例对象 s1、s2,根据输出可以判定 s1 与 s2 相等,即 s1,s2 指向同一块内存地址,是同一个实例。
javascript
class Singleton{
static instance;
show() {
console.log('我是一个单例对象')
}
static getInstance() {
// 判断是否已经new过1个实例
if (!Singleton.instance) {
// 若这个唯一的实例不存在,那么先创建它
Singleton.instance = new Singleton()
}
// 如果这个唯一的实例已经存在,则直接返回
return Singleton.instance
}
}
const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
console.log(s1 === s2);
// true
实际上,基于 ES6 使用模块导入导出,也可以看作单例模式:
javascript
// 比如这是 SingletonExample.js
// 可以定义一些属性,方法等等,然后通过 export 导出,其他模块再分别导入
const array_state = [];
const methodTest = function() {
console.log('这是测试方法')
}
export default {
array_state, methodTest
}
场景举例
利用单例模式思想 来实现 登录框的 显示和隐藏
javascript
class LoginForm {
constructor(){
this.state = 'hide'
}
show(){
if(this.state === 'show'){
console.log('已经显示');
return
}
this.state = 'show';
console.log('登录框已显示')
}
hide(){
if(this.state === 'hide'){
console.log('已经隐藏');
return
}
this.state = 'hide';
console.log('登录框已隐藏')
}
}
LoginForm.getInstance = (function(){
let instance
return function() {
if(!instance){
instance = new LoginForm();
}
return instance
}
})
测试:
javascript
let login1 = LoginForm.getInstance();
login1.show(); //打印:登录框显示成功
let login2 = LoginForm.getInstance();
login2.hide(); //打印:登录框隐藏成功
console.log(login1 === login2)