单例的定义
保证一个类只有一个实例,并且提供一个访问他的全局访问点。
例如:当我们点击登录按钮的时候页面会出现一个登录浮窗,而这个登录浮窗是唯一的,不管用户点击多少次登录按钮,这个浮窗只会被创建一个,这个登录浮窗就很适合使用单例模式来创建。
单例的用途
实现线程池、全局缓存、浏览器的window对象。实际上vuex就是基于单例的思想实现的。
惰性单例
指的是在需要的时候才创建对象实例。而不是在页面加载好的时候才创建。
例如上述例子中我们说到的登录弹窗,可能有些用户访问页面只是想浏览一下,并不需要登录,因此我们不需要在页面加载完成就创建这个弹窗,而是等到用户点击登录的时候才首次创建这个实例,后面再点击的时候都不会再创建弹窗,而是访问之前创建好的唯一弹窗。
代码实现
下面是一个通过创建单例模式来实现创建一个全局使用的登录弹窗的示例
var createLoginLayer = (function () {
var div;
return function () {
if (!div) { // 当div不存在时才创建
div = document.createElement("div");
div.innerHTML = "我是登录浮窗";
div.style.display = "none";
document.body.appendChild(div);
console.log("创建登录弹窗");
}
return div;
};
})();
document.getElementById("loginBtn").onClick = function () {
var loginLayer = createLoginLayer();
loginLayer.style.display = "block";
};
以上代码无论点击多少次登录按钮,都只会打印一次“创建登录弹窗”。
但是如果后续我们想再写一个唯一的窗口用来加载第三方页面,我们需要再次实现一次单例模式。根据代码重用性和单一职责原则,我们可以把创建单例的部分提取出来作为公共代码,而创建登录弹窗部分的代码作为其参数传入。后续需要创建其他的单例也可以只编写业务部分的代码,无需再次编写单例模式部分的代码。
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);
console.log("创建登录弹窗");
return div;
};
var singleCreateLoginLayer = getSingle(createLoginLayer);
document.getElementById("loginBtn").onClick = function () {
var loginLayer = singleCreateLoginLayer();
loginLayer.style.display = "block";
};
后续我们可以直接调用getSingle方法去创建其他的单例