单例模式的定义是:保证只有一个类仅有一个实例,并提供一个访问它的全局访问点
核心:仅有一个实例,提供全局访问
用代理实现单例模式
<!DOCTYPE html>
<head>
<title>用代理实现单例模式</title>
<style>
div {
margin: 10px;
padding: 10px;
border: 2px solid
}
</style>
</head>
<body>
<h1>用代理实现单例模式</h1>
<p><button onclick="handleClick()">创建一个div</button></p>
</body>
<script>
// CreateDiv 是被代理的类,职责:负责创建对象、执行初始化
var CreateDiv = function (html) {
this.html = html
this.insertNode()
}
CreateDiv.prototype.insertNode = function () {
var div = document.createElement('div')
div.innerHTML = this.html
document.body.appendChild(div)
}
// ProxySingletonCreateDiv 是代理类,职责:确保对象是一个单例
var ProxySingletonCreateDiv = (function () {
var instance
return function (html) {
if (!instance) {
instance = new CreateDiv(html)
}
return instance
}
})()
function handleClick () {
var a = new ProxySingletonCreateDiv('You are the only one I care')
var b = new ProxySingletonCreateDiv('I care you too')
console.log([a, b, a===b])
}
</script>
</html>
在上面的代码中,我们让CreateDiv负责创建对象、执行初始化,让ProxySingletonCreateDiv确保对象是一个单例。做到了单一职责原则
然而JS并不是一个以类为中心的语言,JS没有实质上的类的概念,返回一个唯一的对象并不需要事先定义这个对象的构造函数(除非有必须返回某个类的需求)。事实上,我们可以返回任意对象,只要它是唯一的。
用闭包实现单例模式
<!DOCTYPE html>
<head>
<title>用闭包实现单例模式1</title>
<style>
div {
margin: 10px;
padding: 10px;
border: 2px solid
}
</style>
</head>
<body>
<h1>用闭包实现单例模式1</h1>
<p><button onclick="handleClick()">创建一个div</button></p>
</body>
<script>
var SingletonCreateDiv = (function () {
var _nodeElement
var _insertNode = function (html) {
var div = document.createElement('div')
div.innerHTML = html
document.body.appendChild(div)
return div
}
return function (html) {
if (!_nodeElement) {
_nodeElement = _insertNode(html)
}
return _nodeElement
}
})()
function handleClick () {
var a = SingletonCreateDiv('You are the only one I care')
var b = SingletonCreateDiv('I care you too')
console.log([a, b, a===b])
}
</script>
</html>
我们使用闭包封装了一个私有变量 _nodeElement和一个私有方法 _insertNode,通过判断 _instance是否为undefined决定是否插入dom
但是通过私有方法 _insertNode处理单例的逻辑违反单一职责原则,假如下次我不是插入Node节点,而是创建一个唯一的section,一个唯一script标签,或者发送ajax请求......,那么我必须要改动SingletonCreateDiv的内部代码。
改进如下
<!DOCTYPE html>
<head>
<title>用闭包实现单例模式2</title>
<style>
div {
margin: 10px;
padding: 10px;
border: 2px solid
}
section {
margin: 10px;
padding: 10px;
border: 2px dashed
}
</style>
</head>
<body>
<h1>用闭包实现单例模式2</h1>
<p>
<button onclick="handleClick('div')">创建一个div</button>
<button onclick="handleClick('section')">创建一个span</button>
</p>
</body>
<script>
function Singleton (fn) {
var _nodeElement
return function () {
return _nodeElement || (_nodeElement = fn.apply(this, arguments))
}
}
function insertDiv (html) {
var node = document.createElement('div')
node.innerHTML = html
document.body.appendChild(node)
return node
}
function insertSection (html) {
var node = document.createElement('section')
node.innerHTML = html
document.body.appendChild(node)
return node
}
var SingletonCreateDiv = Singleton(insertDiv)
var SingletonCreateSection = Singleton(insertSection)
function handleClick (node) {
if (node === 'div') {
var a = SingletonCreateDiv('You are the only one I care')
var b = SingletonCreateDiv('I care you too')
console.log([a, b, a===b])
} else if (node === 'section') {
var a = SingletonCreateSection('You are the only one I care')
var b = SingletonCreateSection('I care you too')
console.log([a, b, a===b])
}
}
</script>
</html>
小结
- 需要在何时的时候创建对象,并且只创建一个,可以采用单例模式
- 创建对象和管理单例的职责被分布应该符合单一职责原则,我们可以利用闭包和高阶函数来完成。
(finished)