JavaScript设计模式——单例模式

单例模式的定义:一个类只能创建一个实体对象。可能遇到的场景线程池的创建,全局缓存的创建等等。

结合JavaScript的语言特点,如果要实现单例,只需要通过一个全局变量来作为标志位,每次创建对象的时候都去判断一次,如果之前已经创建过则直接返回,没有则new一个出来,上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var instance = null;
var Person = function() {
this.name = 'hahacoo'
}
Person.create = function() {
if(!instance){
instance = new Person()
}
return instance
}
var p1 = Person.create();
var p2 = Person.create();
console.log(p1 === p2); //true

以上就是一个简单的单例模式,但是和以往我们通过new的方式创建一个对象还是有很大的区别。而且全局变量的创建也造成了变量的污染。下面是改进后的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Person = function() {
this.name = 'hahacoo'
}
var ProxySingleton = function(fn) {
var instance;
return function() {
if(!instance) {
return new fn()
}
return instance;
}
})
var SingletonPerson = ProxySingleton(Person);
var p1 = new SingletonPerson();
var p2 = new SingletonPerson();
console.log(p1 === p2); //true

通过代理,将自定义类和单例实现的逻辑分开,符合单一职责的原则,也符合我们的书写习惯。

JavaScript作为一门函数式脚步语言,不仅仅局限在面向对象,对于函数编程如何实现单例模式呢?我们在做web开发时,经常会有这样的需求,对于多次操作只希望有一个dom对象被更改,譬如说对话框,提示框,也同样符合单例模式的特点,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var showDialog = function() {
var dialog = document.createElement('div');
dialog.style.display = 'none';
document.body.appendChild(dialog);
return dialog;
}
var button = document.getElementById('#show');
var singletonDialog = function(fn) {
var dialog;
return function() {
if(!dialog) {
dialog = fn.apply(this, arguments)
}
return dialog
}
}
var dialogDom = singletonDialog(showDialog);
button.onclick = function() {
var dialog = dialogDom();
dialog.style.display = "static";
}

如上,在页面就只会存在一份dom节点,确保不会重复创建。

单例模式的关键就是遵循单一职责原则,将我们的业务逻辑代码单例实现代码分开,灵活使用闭包高阶函数,保证对象的唯一性。