定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
详细描述
在平时开发中来说有些对象或者操作只需要一个(比如事件的绑定,dom节点的创建又或者页面或者组件的创建、事件绑定等),所以这个时候就需要使用单例模式来时所需要的操作只执行一次类节省不必要的开销和资源。
代码实现
因为在js中生成一个对象不需要非得使用一个类进行创建,所以我们只需要保证所创建的对象只有一个并且全局可以访问就可以。因此我们创建类的方式可以采用new 方式也可以采用函数方式,又或者直接定义出来,然后将它缓存起来不被重复创建就可以。下面我们将采用闭包和高阶函数来实现一个js中的单例模式。 代码如下:
/**
*
*
* @param {*} fn 创建对象的函数
* @return {*}
*/
const single = (fn) => {
let instance; // 缓存唯一对象的变量
return function(...res) {
/*
如果没创建过对象则调用传入的函数创建并缓存,如果已经存在创建好的对象直接返回即可。
注意: js中创建对象不一定基于类,所以可以采用函数创建的方式,如果想基于类的方式创建,那么
fn参数传入构造函数,然后使用new 关键字进行调用生成即可。
*/
if (!instance) instance = fn.apply(this, res);
return instance;
}
}
上述代码根据单例模式的定义和思想,实现一个基于js语言特点更加通用的单例模式,使用的时候只需要将生成对象的函数传进去即可。
测试
function domObj (text) {
const div = document.createElement('div');
div.innerText = text;
document.body.appendChild(div);
return div;
}
const singleDom = single(domObj);
console.log(singleDom('hello') === singleDom('world')); // true
应用
1、 创建登录浮框、蒙层、消息弹框、动态元素、组件等都可以使用单例模式进行创建,因为他们在使用的时候都是唯一的,只需使用的时候创建一次就行,不需要重复创建较少系统的性能开销,下面就实现一个唯一的内容展示框,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<button onclick="hanldleClick('block')">显示</button>
<button onclick="hanldleClick('none')">隐藏</button>
</div>
<script>
const single = (fn) => { // 单例
let instance;
return function(...res) {
if (!instance) instance = fn.apply(this, res);
return instance;
}
}
function creatObj() { // 动态创建元素对象
const dom = document.createElement('div');
dom.innerText = '我是展示的内容';
dom.style.display = 'none';
document.body.appendChild(dom);
return dom;
}
const singleDom = single(creatObj);
function hanldleClick (data) { // 响应点击事件
const dom = singleDom();
dom.style.display = data;
}
</script>
</body>
</html>
上述代码就创建了一个唯一的展示内容的节点,不管隐藏还是显示,它只会创建一次。
2、 绑定一次事件。 有时候我们渲染完页面的一个列表后会给这个列表添加点击事件,但是列表的数据是通过ajax多次动态追加的,我们其实只需要在第一次渲染列表的时候添加一次事件即可,如果不想记录当前是否是第一次渲染列表,也可以使用单例模式来实现,代码如下:
const single = (fn) => { // 单例
let instance;
return function(...res) {
if (!instance) instance = fn.apply(this, res);
return instance;
}
}
const render = single(function() { // 绑定事件函数
console.log('绑定事件'); // 只会执行一次
document.getElementById('list').onclick = function() {
console.log('click');
}
return true;
});
render();
render();
render();
render();
总结
在js中严格来说其实是没有类这个概念的,他的类是基于原型模式来实现的,所以它的单例模式又和其他语言(只能由类来创建对象的语言,如java)不太一样,所以我们需要灵活借用单例模式的思想,来优化和实现我们的功能和业务,而不能生搬整个模式。