单例模式实例详解

保证一个特定的类只有一个实例(并且永远是同一个)
在JavaScript中,只有对象没有类,要实现单例模式也有多种方法。

最简单的方式:通过字面量来创建对象,因为在JS中对象之间永远不可能相等,除非它们是同一个对象
使用字面量创建的对象总是唯一的。

实现单例模式的的几种途径

  1. 在代码中提供一个全局变量来存储创建出来的实例。缺点:该全局变量可能会被轻易的修改和覆盖
  2. 尝试在构造函数的静态成员中缓存实例属性。缺点:函数的静态属性在外部可以直接修改,容易导致实例对象的丢失。
  3. 将实例对象包装在闭包中,安全性好,无法轻易修改。缺点:有额外的闭包开销。

    全局变量方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //提供全局变量,使用全局变量来接收内部创建出来的实例对象(this)
    var instance;
    //提供构造函数
    function Singleton () {
    //判断是否已经创建存在过
    if (instance) {
    //如果创建过直接返回
    return instance;
    }
    //默认把实例对象赋值给this再传给全局变量instance
    instance = this;
    //设置实例属性方法
    this.property = '设置属性';
    this.fun = function() {'设置方法'};
    }
    var obj1 = new Singleton();
    var obj2 = new Singleton();
    //两个对象存储数据的地址一样,实际就是一个对象
    console.log(obj1 === obj2); //true

但是上述方法有一个明显的问题,全局变量instance容易被修改,造成之后创建的实例指向错误的数据。

改进:通过即使调用函数,把instance封闭在里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//声明构造函数
var Singleton;
(function () {
//内部声明instance
var instance;
Singleton = function () {
if (instance) {
//如果创建过直接返回
return instance;
}
instance = this;
this.property = '设置属性';
this.fun = function() {'设置方法'};
}
})();

这样只能修改构造函数名称,但是修改了的话浏览器会报错提示我们Singleton is not a constructor,可以很快排错。

构造函数静态属性方法

通过把instance设置为构造函数Singleton的静态属性,在一定程度上可以防止被修改。但是通过Singleton.instance还是可以修改到。

1
2
3
4
5
6
7
8
function Singleton () {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
this.property = '设置属性';
this.fun = function() {'设置方法'};
}

惰性函数方法

实现思路:在构造函数内部声明一个私有变量instance,利用惰性函数执行过一次后自我更新的特性可以直接返回instance

问题:由于惰性函数的自我更新,导致构造函数指向新的对象

在构造函数更新过一次后,新的构造函数创建出来的实例的constructor属性还是跟第一次创建出来的实例一样,指向更新前的构造函数。并且创建对象以后设置的原型对象和单例对象不是同一个(由于更新之后,新构造函数的原型对象与旧构造函数的原型对象不是同一个),导致创建对象后设置到原型对象的属性和方法无法访问。

改进方法:利用原型链继承,将新构造函数原型对象设置为旧构造函数的实例。同时用新构造函数创建实例赋值给instance,并修正instance构造器属性指向。

步骤:

1. 提供一个构造函数
2. 在构造函数内部提供一个私有变量
3. 利用惰性函数实现构造函数的自我更新(直接返回instance)
4. 设置新构造函数的原型对象是旧构造函数的一个实例
5. 使用新构造函数创建对象并赋值给instance
6. 修正构造器属性指向更新后的构造函数
7. 使用instance设置属性和方法
8. 返回instance

惰性函数单例模式

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
//提供单例构造函数
function Singleton () {
/*系统在新建Singleton的实例对象时,会有如下默认操作
var 实例对象
this = 实力对象
所以在第一次执行时,this就代表最开始这个构造函数的实例对象*/
//提供私有变量
var instance;
//惰性函数执行一次后自我更新
Singleton = function () {
return instance;
};
//自我更新后,Singleton代表新构造函数,this指向旧构造函数的实例
//原型链继承,实现原型属性访问修改
Singleton.prototype = this;
//设置私有变量为新构造函数的实例对象
instance = new Singleton();
//修正构造器属性指向更新后的构造函数
instance.constructor = Singleton;
//设置属性方法
instance.property = '设置属性';
instance.fun = function () {'设置方法'};
//返回这个instance,覆盖this
return instance;
}