设计模式-谁没遇见过几个单例模式(一)

6,471 阅读3分钟

一、什么是单例模式

单例模式的定义是,保证一个类仅有一个实例,并提供一个访问它的全局访问点。

有一些对象,比如线程池/全局缓存/浏览器中的 window 对象等等,我们只需要一个实例。下面将根据实际场景进行介绍。

二、实际场景

1. 登录浮窗

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

1.1 传统做法

传统做法在页面加载完成时,就创建好登录浮窗,当用户点击登录按钮时,显示登录浮窗,实现代码如下:

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>
  
	<button id="loginBtn">登录</button>
	<script type="text/javascript" src="index.js">
		
	</script></body></html>
var loginLayer = (() => {
	let div = document.createElement('div')
	div.innerHTML = '我是登录弹窗'
	div.style.display = 'none'

	document.body.appendChild(div)

	return div
})()

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

源码地址

上述代码有以下缺点:

  1. 在无需登录的情况下,也会新增登录浮窗的 DOM 节点,浪费性能。

现在优化一下,将代码改为,在用户点击登录按钮后,才新增登录浮窗的 DOM 节点。

代码如下:

var createLoginLayer = () => {
	let div = document.createElement('div')
	div.innerHTML = '我是登录弹窗'
	div.style.display = 'none'

	document.body.appendChild(div)

	return div
}

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

源码地址

上述代码也存在缺陷,具体如下:

  1. 每次点击登录按钮,都会创建一个登录浮窗,频繁的创建 DOM 节点更加浪费性能。

实际上,我们只需要创建一次登录浮窗。

1.2 单例模式

通过单例模式,重构上述代码。

const createLoginLayer = () => {
	const div = document.createElement('div')
	div.innerHTML = '我是登录弹窗'
	div.style.display = 'none'
	console.log(123)

	document.body.appendChild(div)
	return div
}

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

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

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 等其他标签时,可以直接复用该逻辑。

三、总结

单例模式是一种简单但非常实用的模式,特别是惰性单例技术,在合适的时候才创建对象,并且只创建唯一的一个。更奇妙的是,创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。


· 往期精彩 ·

【设计模式-谁没遇见过几个单例模式(一)】

【设计模式-什么是快乐星球,什么是策略模式(二)】

【设计模式-原来这就是代理模式(三)】

【设计模式-简单易懂的观察者模式(四)】

【设计模式-不会吧,不会还有人不知道装饰器模式吧(五)】