内容大部分来自 《javascript 设计模式与开发实践》一书,适当的结合自己工作实践,做出整理和笔记
系列: javascript 设计模式
单例模式
单例模式的定义是产生一个类的唯一实例。
- 单例模式只能有一个实例。
- 对所有的访问都提供这一个单例。
传统语言的单例模式
1 | class BaseSingle { |
这样写存在缺点:
- 不易于扩展为非单例、
- 方法同时处理了两件事,一个保证方法init的执行,另外保证只有一个对象,这违背了
单一职责
原则
代理单利模式
为了解决上述存在的问题,引入代理单例模式的方法,通过一个代理方法来讲类本身和实现代理的逻辑隔离开来。如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 代理单利模式,单例部分抽离出来
class BaseSingle2 {
constructor() {
this.init();
}
init() {
// console.log('ok ...');
}
}
/* 通过闭包 和 return 复杂结构实现 */
const ProxySingleEs5 = (() => {
// 闭包保存变量
let instance;
return function PrxSingle(...args) {
if (!instance) {
instance = new BaseSingle2(args);
}
return instance;
};
})();
console.log('引入代理实现的单例模式:', new ProxySingleEs5() === new ProxySingleEs5());
这里有两个知识点:
- 一个是闭包
- 一个是在执行new 关键字的时候,如果构造函数返回了一个对象,那么new 会直接返回改对象,否则返回构造函数中的this
JS 中的单例模式
前面提到的是在传统面向对象语言中的实现,单例的对象从类中而来,在js中可以直接把变量当做单例使用。1
2
3
4
5
6
7
8
9
10
11const userSingle = (() => {
const name = 'name - xx';
const age = 180;
return {
getUser() {
return `${name} : ${age}`;
}
};
})();
console.log('userSingle:', userSingle.getUser());
通过闭包避免了对全局作用域的污染
JS惰性单例
在需要的时候才创建出对象的实例,这种也是日常开发中最有用的场景。在前面的 传统语言的单例模式 已经使用了惰性,这里换成更适合js语言的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 通用的惰性单例实现
const getSingle = function getSignle(fn) {
let result;
return function proxySingle(...args) {
if (!result) {
result = fn.apply(this, args);
}
return result;
}
}
// 例:创建一个元素,避免多次创建
const createDom = function createDom() {
const domDiv = document.createElement('div');
domDiv.innerHTML = '我创建的DOM 节点';
document.body.appendChild(domDiv);
return domDiv;
};
const singleCreateDom = getSingle(createDom);
console.log('singleCreateDom:', singleCreateDom() === singleCreateDom());
惰性单例模式在实际的开发中有大量的应用
- 比如在多个模块都依赖一个接口服务,需要但是有不确定调用顺序只用讲接口调用方法单例化
- 比如需要保证多次创建dom后绑定事件的方法只会执行一次
- …