单例模式
# JS设计模式总结笔记-单例模式
分类:创建类模式
# 概念
单例模式定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点;
对于单例模式,就是一个类只能有一个实例。其中类这一概念对于前端JS而言,是一个比较神奇的存在,它既是一个funciton又是一个Class(ES6),其实Class也只是Function的语法糖。对于这样每一个类我们只能有一个对应的对象,这就是单例模式。
# 实现理念
在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。
在 js 开发中,我们经常会把全局变量当成单例来使用
# 最简单的对象字面量
const A = {
name: 'maoyl',
age: 12
}
const t1 = A;
const t2 = A;
console.log(t1 === t2); // true
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
其实对象的直接赋值也是单例模式,A负值给t1 和t2只是修改了它的指向。 个人理解:有错联系修正。
# 单例模式的多种实现方案
单例模式很简单,下面我们之间看几个实例:
方案一
- 利用instanceof判断是否使用new关键字调用函数进行对象的实例化
function Singleton() {
if (!(this instanceof Singleton)) return;
if (!Singleton._instance) {
this.name = 'maoyl';
Singleton._instance = this;
}
return Singleton._instance;
}
const singleton1 = new Singleton();
const singleton2 = new Singleton();
console.log(singleton1 === singleton2); // 返回true
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
方案二
- 在函数上直接添加方法属性调用生成实例
function Singleton() {
this.name = 'maoyl';
}
Singleton.getInstance = function() {
if (!Singleton._instance) {
Singleton._instance = new Singleton();
}
return Singleton._instance;
}
const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();
console.log(singleton1 === singleton2); // 返回true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
方案三
- 使用闭包(改进
方式二)
function Singleton() {
this.name = 'maoyl';
}
Singleton.getInstance = (function() {
let instance;
return function() {
if (!instance) {
instance = new Singleton();
}
return instance;
}
})();
const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();
console.log(singleton1 === singleton2); // 返回true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
方式四
- 使用包装对象结合闭包的形式实现(改进
方式三)
const Singleton = (function () {
function _singleton() {
this.name = 'maoyl';
}
return function() {
console.log(_singleton);
console.log(_singleton.instance);
if (!_singleton.instance) {
_singleton.instance = new _singleton();
}
return _singleton.instance;
}
})();
const singleton1 = new Singleton();
const singleton2 = new Singleton();
console.log(singleton1 === singleton2); // 返回true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在流程比较复杂的情况下,闭包里的东西也是可以提取出来单独封装的
在频繁使用到单例的情况下,推荐使用类似此方法的方案,当然内部实现可以采用上述任意一种
function SingleWrapper(cons) {
// 排除非函数与箭头函数
if (!(cons instanceof Function) || !cons.prototype) {
throw new Error('不是合法的构造函数')
}
var instance
return function () {
if (!instance) {
instance = new cons()
}
return instance
}
}
function Singleton(){
this.name = 'xm'
}
const MySingleton = SingleWrapper(Singleton)
const singleton1 = new MySingleton()
const singleton2 = new MySingleton()
console.log(singleton1 === singleton2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
方式五
- 在构造函数中利用new.target判断是否使用new关键字
class Singleton {
constructor(){
if (new.target === ! Singleton) {
return;
}
if (!Singleton._instance) {
this.name = 'maoyl';
Singleton._instance = this;
}
return Singleton._instance;
}
}
const singleton1 = new Singleton();
const singleton2 = new Singleton();
console.log(singleton1 === singleton2); // 返回true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
new.target学习 (opens new window)
new.targe:
new.target 属性允许你检测函数或构造方法是否是通过new运算符被调用的。在通过new运算符被初始化的函数或构造方法中,new.target返回一个指向构造方法或函数的引用。在普通的函数调用中,new.target 的值是undefined。
方式六
- 使用static静态方法
class Singleton {
constructor(){
this.name = 'maoyl';
}
static getInstance() {
if (!Singleton._instance) {
Singleton._instance = new Singleton();
}
return Singleton._instance;
}
}
const singleton1 = new Singleton.getInstance();
const singleton2 = new Singleton.getInstance();
console.log(singleton1 === singleton2); // 返回true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 单例模式优缺点
优点:
- 提供了对唯一实例的受控访问。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。
缺点:
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
编辑 (opens new window)
上次更新: 2025/04/18, 01:42:12