第四章-单例模式
cooljser 2020-06-08
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。例如:线程池、全局缓存、浏览器中的 window 对象等。实际场景中,比如要实现一个登陆弹窗,这个弹窗只会被创建一次,那么就可以使用单例模式来生成。
# 实现单例模式
最基础的版本,就是使用一个变量来标识当前对象是否已创建,若已创建则直接返回之前创建的对象,没有创建则创建后返回,示例代码如下:
var Singleton = function (name) {
this.name = name;
this.instance = null;
};
Singleton.prototype.getName = function () {
return this.name;
};
Singleton.getInstance = function (name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
};
var a = Singleton.getInstance('jack');
var b = Singleton.getInstance('jack');
console.log(a === b); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
上面这个示例中的 Singleton 类是"不透明"的,因为调用者必须知道这是个单例类,并且使用 Singleton.getInstance 才能获取到单例对象。
# 透明的单例模式
现在我们来实现一个"透明"的单例类,让使用者可以像使用其他类一样,从这个类中创建对象,示例代码如下:
var CreateDiv = (function () {
var instance;
var CreateDiv = function (html) {
if (instance) {
return instance;
}
this.html = html;
this.init();
return (instance = this);
};
CreateDiv.prototype.init = function () {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.append(div);
};
return CreateDiv;
})();
var a = new CreateDiv('jack');
var b = new CreateDiv('jack');
console.log(a === b); // true
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
虽然看起来完成了一个"透明"的单例类,但是它也有一些缺点。为了把 instance 封装起来,我们使用了一个自执行的匿名函数和闭包,并且这个匿名函数返回了真正的 Singleton 构造方法,这无疑增加了程序的复杂度,也让代码阅读起来很困难。
另外 CreateDiv 的构造函数也违背了"单一职责"原则,这个函数做了两件事情,一是创建对象,二是执行初始化函数。
# 用代理实现单例模式
现在我们通过引入代理类的方式,来解决上面的问题。
var CreateDiv = function (html) {
this.html = html;
this.init();
};
CreateDiv.prototype.init = function () {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.append(div);
};
var ProxySingletonCreateDiv = (function () {
var instance;
return function (html) {
if (!instance) {
instance = new CreateDiv(html);
}
return instance;
};
})();
var a = new ProxySingletonCreateDiv('jack');
var b = new ProxySingletonCreateDiv('jack');
console.log(a === b); // true
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上述代码中,通过引入代理类的方式,实现了一个单例模式的编写,跟之前不一样的是,现在我们把负责管理单例的逻辑迁移到了代理类 ProxySingletonCreateDiv 中。这样一来,CreteDiv 就变成了一个普通的类,它跟 ProxySingletonCreateDiv 组合起来就可以达到单例模式的效果。
# 通用的惰性单例
惰性单例指的是在需要的时候才创建对象单例,如下示例:
var getSingle = function (fn) {
var result;
return function () {
return result || (result = fn.apply(this, arguments));
};
};
var createLoginLayer = function () {
var div = document.createElement('div');
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
};
var createSingleLoginLayer = getSingle(createLoginLayer);
// 如果要创建唯一的 ifram,可以这么写
var createSingleIframe = getSingle(function () {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
return iframe;
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
这种单例模式的用途不至于创建对象,也可以作用于任何的其他函数,比如绑定事件等等。