设计模式 - 单例模式

Posted by luoxiao on 2021-07-13

什么是单例模式?

单例模式的定义是,保证一个类仅有一个实例,并提供一个访问它的全局访问点。又被称为单体模式,是只允许实例化一次的对象类,有时候我们也用一个对象来规划一个命名空间,井井有条的管理对象上的属性与方法。

实现方式

实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。

场景

命名空间和代码库

1
2
3
4
5
6
7
8
9
10
11
12
13
var A = {
Util: {
util_mothod1: function() {},
util_mothod2: function() {},
},
Ajax: {
get: function() {},
post: function() {},
},
}

A.Util.util_mothod1()
A.Ajax.get()

静态变量的管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Conf = (function() {
// 私有变量
var conf = {
MAX_NUM: 100,
MIN_NUM: 1,
}
return {
// 取值器方法
get: function(name) {
return conf[name] ? conf[name] : null
}
}
})()

var num = Conf.get('MAX_NUM')

惰性单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var LazySingle = (function() {
// 单例实例
var _instance = null

// 单例
function Single() {
// 私有属性和方法
return {
publicMethod: function() {},
publicProperty: '1',
}
}

// 获取单例对象接口,调用的时候才会返回
return function () {
if (!_instance) {
_instance = Single()
}
return _instance
}
})()

LazySingle().publicProperty

登录浮窗的设计

当我们单击登录按钮时,页面中会出现一个登录的浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

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
36
const createLoginLayer = () => {
const div = document.createElement('div')
div.innerHTML = '我是登录弹窗'
div.style.display = 'none'
console.log(123)

document.body.appendChild(div)
return div
}

const createIframe = function () {
const iframe = document.createElement('iframe')
document.body.appendChild(iframe)
iframe.style.display = 'none'
return iframe
}

const createSingle = (function () {
var instance = {}
return function (fn) {
if (!instance[fn.name]) {
instance[fn.name] = fn.apply(this, arguments)
}
return instance[fn.name]
}
})()

const createSingleLoginLayer = createSingle(createLoginLayer)
const createSingleIframe = createSingle(createIframe)

document.getElementById('loginBtn').onclick = () => {
const loginLayer = createSingleLoginLayer
const iframe = createSingleIframe
loginLayer.style.display = 'block'
iframe.style.display = 'block'
}

1、将创建实例对象 createLoginLayer / createIframe 的职责和管理单例对象 createSingle 的职责分离,符合单一职责原则;
2、通过闭包存储实例,并进行判断,不管点击登录按钮多少次,只创建一个登录浮窗实例;
3、易于扩展,当下次需要创建页面中唯一的 iframe / script 等其他标签时,可以直接复用该逻辑。

其他使用方式

用于参数的传递:

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
36
var SingletonTester = (function () {

//参数:传递给单例的一个参数集合
function Singleton(args) {

//设置args变量为接收的参数或者为空(如果没有提供的话)
var args = args || {};
//设置name参数
this.name = 'SingletonTester';
//设置pointX的值
this.pointX = args.pointX || 6; //从接收的参数里获取,或者设置为默认值
//设置pointY的值
this.pointY = args.pointY || 10;

}

//实例容器
var instance;

var _static = {
name: 'SingletonTester',

//获取实例的方法
//返回Singleton的实例
getInstance: function (args) {
if (instance === undefined) {
instance = new Singleton(args);
}
return instance;
}
};
return _static;
})();

var singletonTest = SingletonTester.getInstance({ pointX: 5 });
console.log(singletonTest.pointX); // 输出 5