singleton是指对于一个特定的类,只会产生一个实例。这就是说,当你第二次用这个class去创建一个新对象时,你会得到第一次创建的那个对象。在JS中,要如何实现呢?在JS中没有类,当我们创建一个新的对象时,这个对象实际上就是singleton.当我们用直接量来创建一个对象时,这实际上就是一个新的singleton实例:
var obj= {
myprop: 'my value'
}
Using new
JS中没有类,所以照字面意思来套用singleto是没有什么意义的。但是在JS中可以用new通过constructor来创建对象,而当你用new来创建一系列对象的时候,如果想得到返回的是同一个对象的指针,singleton就派上用场了。
我们可以利用下面三种方式来创建一个singleton:
- 我们可以用一个全局变量来存储这个实例,当然,这并不是一种值得推荐的方式 ,因为用全局变量是一个非常槽的主意。
- 可以用一个静态属性来保存这个实例,不过有一个小缺陷,就是这个属性是全局可以访问的。
- 你可以用闭包来保存这个实例,这个可以保证这是一个私有的变量,外部的代码并不能修改它。
Instance in a static property
function Universe() { if(typeof Universe.instance === 'object') { return Univese.instance; } //添加一些属性 this.start_time = 0; Universe.insance = this; } //testing var uni = new Universe(); var uni2 = new Universe(); uni === uni2; //true Instance in a clousre function Universe() { var instance = this; this.start_time = 0; Universe = function() { return instance; } } //testing var uni = new Universe(); var uni2 = new Universe(); uni === uni2;//true
让我们来看一下上面的代码存在着什么问题,
Universe.protoype.nothing = true; var uni = new Universe(); Univere.protoype.everything = true; var uni2 = new Universe(); //Testing; uni.nothing;//true uni2.nothing;//true; uni.everything;//undefined uni2.everything;//undefined uni.constructor.name;//"Universe" uni.connstructor === Universe;//false
上面的代码为什么表现的如此怪异呢,这是因为uni.constructor指向的那个是原先的那个Universe()而不是重新定义的那个Universe()
让我们来看一个新的改良过的:
function Universe() { //the cached instance var instance; //重新覆写constructor Universe = function Universe() { return instance; } //重新覆写prototype properties Universe.prototype = this; //the instance instnace = new Universe(); instance.constructor = Universe; instance.start_time = 0; instance.bang = "big"; return instance }
如果我们再用上面的测试用例重新测一下,就会发现这是正确的了。
我们再看一种新实现方式,把constrcutor和instance都包在一个immediate function中,当第一次调用构造函数的时候,它创造一个对象,instance会指向它。从第二次调用,constructor仅会简单的返回这个么有变量(instance)
var Universe; (function(){ var instance; Universe = function Universe() { if(instance) { return instance; } instance = this; //all the functionality this.start_time = 0; this.bang = "Big"; }; }());