1. 概述
单例模式(Singleton,也称单件模式、单体模式)是对象创建型模式,主要用于保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式是一种常见的设计模式。
要使用单例模式,在某些场景显得尤为重要。比如要使用迅雷播放器播放视频,我同时打开两个视频文件,如果这两个视频文件分别在各自的播放器窗口放映视频内容,那么可能会有很不好的观看体验,我没办法分别让左右耳朵和左右眼睛只专注各自对应的视频。这时候,我们就需要单例模式来解决这个问题了:即一次只能打开一个迅雷播放器窗口。
在 windows 操作系统中还有很多单例的例子,比如任务管理器、回收站等。
2. 实现
单例模式是保证一个类仅有一个实例,那么传统做法是先判断实例是否存在,如果已经存在,则直接返回已存在实例,如果不存在就先创建实例后再返回,这就保证了唯一性。当然除了传统做法外,在 JavaScript 中要实现单例模式还有很多其它方法。
2.1 传统实现
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
<script type="text/javascript">
var Player = function(){
this.currentVideo = null;
this.instance = null;
};
Player.prototype.play = function(videoName){
this.currentVideo = videoName;
console.log("播放:" + this.currentVideo);
}
Player.getInstance = function(){
if (!this.instance){
this.instance = new Player();
console.log("第一次启动播放器");
} else {
console.log("使用已打开的播放器");
}
return this.instance;
}
Player.getInstance().play("红楼梦 01.rmvb");
Player.getInstance().play("猫和老鼠 川话版 01.rmvb");
Player.getInstance().play("复仇者联盟.mkv");
</script>
定义保存单例对象的属性,在 getInstance() 方法中判断该属性是否保存有对象,如果已保存则直接返回,否则创建对象后再返回。
执行结果:
1
2
3
4
5
6
第一次启动播放器
播放:红楼梦 01.rmvb
使用已打开的播放器
播放:猫和老鼠 川话版 01.rmvb
使用已打开的播放器
播放:复仇者联盟.mkv
2.2 闭包的实现
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
<script type="text/javascript">
var Player = function(){
this.currentVideo = null;
};
Player.prototype.play = function(videoName){
this.currentVideo = videoName;
console.log("播放:" + this.currentVideo);
}
Player.getInstance = (function(){
var instance;
return function(){
if (!instance){
instance = new Player();
console.log("第一次启动播放器");
} else {
console.log("使用已打开的播放器");
}
return instance;
}
})();
Player.getInstance().play("红楼梦 01.rmvb");
Player.getInstance().play("猫和老鼠 川话版 01.rmvb");
Player.getInstance().play("复仇者联盟.mkv");
</script>
getInstance() 方法中,使用闭包将 instance 变量所保存的实例对象一直缓存在内存中,当第一次调用先创建对象,内存缓存该对象实例信息,第二次或多次调用时,可以直接使用已缓存值。
2.3 直接量
其实最简单的单例就是对象直接量(字面量):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/javascript">
var player = {
currentVideo : null,
play : function(){
console.log("播放视频:" + this.currentVideo);
}
};
player.currentVideo = "红楼梦";
player.play();
player.currentVideo = "西游记";
player.play();
</script>
2.4 闭包的变式
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
30
31
32
<script type="text/javascript">
var Player = (function(){
var instance;
var init = function(){
return {
currentVideo : null,
play : function(videoName){
this.currentVideo = videoName;
console.log("开始播放视频:" + videoName);
}
}
};
return {
getInstance : function(){
if (!instance) {
instance = init();
console.log("第一次启动播放器");
} else {
console.log("使用已打开的播放器");
}
return instance;
}
};
})();
Player.getInstance().play("红楼梦 01.rmvb");
Player.getInstance().play("猫和老鼠 川话版 01.rmvb");
Player.getInstance().play("复仇者联盟.mkv");
</script>