JavaScript设计模式(1)——单例模式

在 JavaScript 中单例模式的应用场景还是比较多的,实现起来也比较简单。大体思路无非就是,第一次调用构造函数时,实例化示例并保存起来。再次调用时直接将第一次实例化的对象返回即可。

实现

一个极简的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var __instance;
function Single() {
if (__instance) {
return __instance;
}
this.name = 'single';
this.time = Date.now();
__instance = this;
return this;
}
// 验证
var obj1 = new Single();
var obj2 = new Single();
obj1 === obj2 // true

上面使用 __instance 存储第一次调用构造函数时实例化的对象,以后每次调用构造函数都直接返回该对象。实现了单例。

但上例有一个明显的缺点,就是容易造成全局变量的污染,使得 __instance 这个变量并不安全,很容易被外部修改。

当然,如果使用了模块化开发则不用担心该问题,完全可以使用上面的实现方法,简单有效。

一个改进的实现方法

针对全局变量污染的问题,我们有很多办法解决这个问题。比如使用不同的命名空间或决定好命名规则,来避免变量被意外修改。除此之外,我们还可以使用闭包来解决这问题:

1
2
3
4
5
6
7
8
function Single() {
this.name = 'single';
this.time = Date.now();
var instance = this;
Single = function() {
return instance;
}
}

上面的写法,通过第一次调用构造函数时改写构造函数来来实现单例。利用了函数闭包的特性,将示例保护起来,不会被外部访问和改写。

在更严格的情况下,我们仍可以对上述例子进行近一步的优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Single() {
this.name = 'single';
this.time = Date.now();

var instance;
Single = function() {
return instance;
}

Single.prototype = this.__proto__;
Single.prototype.consctructor = Single;

instance = this;

return this;
}

上面的优化在于,保证能够在实例化后再次为添加类方法时能够得到继承,同时能够保证每次返回的实例化对象的构造函数指向同一个函数。一般情况下可以不用做次考虑。

总结

在大多数情况下实现单例模式时很简单的。尤其是使用了模块化时,只需使用一个变量将第一次实例化的对象保存起来,再次调用直接返回即可。