小蜗熊的蜂蜜罐
JavaScript设计模式简介之单例模式
发布于: 2020-09-05 更新于: 2023-02-14 分类于: 技术 > Web 阅读次数: 

休整了一阵子,身体和心理都慢慢恢复了一些。鸽了这么久终于补上这篇单例模式了,下面开始正题。
单例模式,顾名思义即一个类仅有一个实例。单例模式下即使多次调用构造函数(使用new操作符创建多个对象),得到的都是同一个实例对象。

使用静态属性的单例模式

使用构造函数本身的静态属性可以实现此功能,方法如下:

1
2
3
4
5
6
7
8
9
10
11
function ClassA() {
if (typeof ClassA.instance === 'object') {
return ClassA.instance
}
ClassA.instance = this
return this
}

var ins1 = new ClassA()
var insi2 = new ClassA()
ins1 === ins2 // true

此方法的缺点在于静态属性instance可以在外部公开访问和修改,为解决此问题可以使用下面的方法。

使用闭包的单例模式

闭包方式

使用闭包进行封装可以确保instance变量的私有性,缺点是增加了额外的闭包开销。

1
2
3
4
5
6
7
8
9
10
function ClassB() {
var instance = this
ClassB = function() { // 重写构造函数
return instance
}
}

var ins1 = new ClassB()
var ins2 = new ClassB()
ins1 === ins2 // true

原型链的继承关系

使用上面的闭包方式存在一个问题:由于构造函数被重写,在初始定义和重定义之间添加到类中的属性都会丢失。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function ClassC() {
var instance = this
ClassC = function() {
return instance
}
}

ClassC.prototype.pro1 = true
var ins1 = new ClassC()
ClassC.prototype.pro2 = true
var ins2 = new ClassC()

console.log(ins1.pro1) //true
console.log(ins1.pro2) //underfined
console.log(ins2.pro1) //true
console.log(ins2.pro2) //underfined
console.log(ins1.constructor.name) // "ClassC"
console.log(ins1.constructor === ClassC) // false

造成这种现象的原因是,重写构造函数后原先的构造函数因为使用了闭包还保留在内存中,但是构造函数指针已经指向了一个新的函数了。
与此同时ins1.constructor仍然指向原始的构造函数而不是重定义之后的构造函数,因此ins1.constructor === ClassC。原型链的继承关系可参见下面两个图:

classDiagram Class --> ClassPrototype ClassPrototype ..> Class ClassPrototype <-- ins1: Prototype ClassPrototype <-- ins2: Prototype class Class{ prototype } class ClassPrototype{ constructor } class ins1{ prototype } class ins2{ prototype }

原型链修复

对于上面提到的问题,我们需要对原型链进行修改。方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function ClassC() {
var instance = this
ClassC = function() {
return instance
}
//新构造函数的原型对象指向原来的原型对象
ClassC.prototype = this.constructor.prototype
//原型对象指回新构造函数
instance.constructor = ClassC
}

ClassC.prototype.pro1 = true
var ins1 = new ClassC()
ClassC.prototype.pro2 = true
var ins2 = new ClassC()

console.log(ins1.pro1) //true
console.log(ins1.pro2) //true
console.log(ins2.pro1) //true
console.log(ins2.pro2) //true
console.log(ins1.constructor.name) // "ClassC"
console.log(ins1.constructor === ClassC) // true

后记

简简单单把这篇的内容讲完了,虽然感觉还有很多细节没来得及说明,就这样吧。该填的坑总算填完了,以后再也不立flag了。设计模式剩下的几篇什么时候更新,随缘吧。

--- 本文结束 The End ---