单例模式

单利模式:保证一个类只有一个实例,并提供一个访问它的全局访问点。好今天我们就来领会下这个单例模式

实现单例模式:

就是利用一个变量来标记当前是否已经为某个类创建的对象,如果是,则在下次获取该类实例时,直接返回之前创建的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var Singleton = function(name){
this.name = name;
this.instance = null;
};
Singleton.prototype.getName = function(){
console.log( this.name )
}

Singleton.getInstance = function(name){ //这里是类的静态方法

if( !this.instance ){ //判断类的静态属性instance是否存在
this.instance = new Singleton(name);
}
return this.instance;
};

var a = Singleton.getInstance('zhu');
var b = Singleton.getInstance('wang');

console.log( a === b ) //true

不过上面这个实现并不友好:通过Singleton.getInstance来获Singleton类的唯一对象,但是增加了类的不透明性,Singleton类的使用者必须知道其为单例类,,不能通过new XXX的形式来获取对象><

实现透明单例模式:

透明单例就是用户可以像使用普通类一样,从这个类中创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var CreateDiv = (function(){
var instance;
var CreateDiv = function( html ){
if( instance ){
return instance;
}
this.html = html;
this.init();
console.log(this)
return instance = this;
};
CreateDiv.prototype.init = function(){
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild( div );
}

return CreateDiv;
}())


var a = new CreateDiv('zhu');
var b = new CreateDiv('wang');
console.log( a===b ) //true

但是上面的代码也同样存在缺点,看下面这个代码:

1
2
3
4
5
6
7
8
9
var CreateDiv = function( html ){
if( instance ){
return instance;
}
this.html = html;
this.init();
console.log(this)
return instance = this;
};

他违反了单一职责原则:既要创建对象,并进行初始化方法,又要保证只有一个对象>><<

用代理实现单例模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var CreateDiv = function( html ){
this.html = html;
this.init();
};
CreateDiv.prototype.init = function(){
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild( div );
}

//引入代理类
var ProxySingletonCreateDiv = (function(){
var instance;
return function(html){
if(!instance){
instance = new CreateDiv(html);
}
return instance;
}
}())

var a = new ProxySingletonCreateDiv('zhu');
var b = new ProxySingletonCreateDiv('wang');
console.log( a === b ) //true

将负责管理单例的逻辑放到独立的一个类,,使原来的createDiv成为一个普通类

javascript中的单例模式

js中的全局变量很容易造成变量污染,用如下的方式可以减少命名污染

  • 使用命名空间
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
33
var nameSpace = {
a : function(){
alert(1);
},
b:function(){
alert(2);
}
};

//动态创建命名空间
var Myapp = {};
Myapp.nameSpace = function(name){
var parts = name.split('.');
var current = Myapp;
for(var i in parts){
if(!current[ parts[i] ]){
current[ parts[i] ] = {};
}
current = current[ parts[i] ];
}
};
Myapp.nameSpace('event');
Myapp.nameSpace('dom.style');
console.dir(Myapp)

/*上面代码等价于:
Myapp = {
event : {},
dom:{
style:{}
}
}
*/

  • 使用闭包封装私有变量

    惰性单例:

    在需要的时候才去创建实例对象
    假设我们现在要创建一个页面上唯一一个登入框,这是我们就可以使用单例模式,另外,我们希望当我们点击页面上的一个按钮后,这个登入框才显示出来:

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
33
34
35
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<link rel="stylesheet" href="">
</head>
<body>
<button id='loginBtn' >登入</button>

<script>
var createQQ = (function(){
var div ;
return function(){

if(!div){
div = document.createElement('div');
div.innerHTML = '你正在登陆';
div.style.display = 'none';
document.body.appendChild(div);
}
return div;
}
}());

document.getElementById('loginBtn').onclick = function(){
var loginLayer = createQQ();
loginLayer.style.display = 'block'
}*/

//上面代码仍违反单一职责原则,,而且复用性不强
</script>

</body>
</html>

通用的惰性单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var createDiv = function(){
var div = document.createElement('div');
div.innerHTML = '你正在登陆';
div.style.display = 'none';
document.body.appendChild(div);
return div;
};

var getSingleton = function(fn){ //将判断单例的逻辑封装成一个函数
var result;
return function(){
return result || (result = fn.apply(this,arguments));
}
};

var createSingleLoginLayer = getSingleton( createDiv );
document.getElementById('loginBtn').onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block'
}

另外tom大叔也有一篇关于单例模式的介绍,可以去学习学习

文章目录
  1. 1. 实现单例模式:
  2. 2. 实现透明单例模式:
  3. 3. 用代理实现单例模式:
  4. 4. javascript中的单例模式
  5. 5. 惰性单例:
  6. 6. 通用的惰性单例