设计模式-单例模式

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

单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等。

试想一下这种场景:

当我们点击登录按钮,页面就出现一个登录弹窗,而弹窗是唯一的,每次点击登录都会弹出一个相同的弹窗。

先看一下下面的实现

 1 var loginLayer = (function(){
 2     var div = document.createElement('div');
 3     div.innerHTML = '我是登录弹窗';
 4     div.style.display = 'none';
 5     document.body.appendChild(div);
 6     return div;
 7 })()
 8 
 9 document.getElementById('loginBtn').onclick = function(){
10     loginLayer.style.display = 'block';
11 }

虽然每次点击登录按钮都能实现显示登录弹窗,但是即使没有点击登录也会创建登录窗口的dom,就会造成浪费。

 

那对上面出现的问题进行优化一下吧

 1 function createLoginLayer(){
 2     const div = document.createElement('div');
 3     div.innerHTML = '我是登录弹窗';
 4     div.style.display = 'none';
 5     document.body.appendChild(div);
 6     return div;
 7 }
 8 
 9 document.getElementById('loginBtn').onclick = () => {
10     const loginLayer = createLoginLayer();
11     loginLayer.style.display = 'block'
12 }

 

上面的代码解决了初始化创建登录dom的问题,但是又出现另一个问题,就是每次点击登录都会创建一个登录浮窗,频繁创建Dom造成浪费。实际上我们只要创建一个登录浮窗。

单例模式

 1 var createLoginLayer = (function(){
 2     let div;
 3     return function(){
 4         if(!div){
 5             div = document.createElement('div');
 6             div.innerHTML = '我是登录弹窗';
 7             div.style.display = 'none';
 8             document.body.appendChild(div);
 9         }
10         return div
11     }
12 })()
13 
14 document.getElementById('loginBtn').onclick = function(){
15     var loginLayer = createLoginLayer()
16     loginLayer.style.display = 'none';
17 }

 

这种方式解决了上面出现的问题,是一种比较理想的实现方式。

但是这段代码同样违反单一职责原则的,创建对象和管理单例都放在createLoginLayer内部,再比如我们如果需要再创建一个iframe,就需要照抄一份createLoginLayer 函数,至此我们需要把不变的部分分离开来,管理单例的逻辑进行抽象,逻辑始终是一样的

// createSingle 用来管理单例
const createSingle = (function(){
    const instance = {}
    return function(fn){
        console.log('fn', fn.name)
        if(!instance[fn.name]){
            instance[fn.name] = fn.apply(this, arguments)
        }
    }
})()

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

const createLoginLayer = function(){
    const div = document.createElement('div');
    div.innerHTML = this.name;
    div.style.display = 'none';
    document.body.appendChild(div);
}
document.getElementById('loginBtn').onclick = () => { 
  const createSingleLoginLayer = createSingle(createLoginLayer.bind({name: '我是登录弹窗'}))
  const createSingleIframe = createSingle(createIframe)
  createSingleLoginLayer.style.display = 'block'
  createSingleIframe.style.display = 'block'
}

将创建createLoginLayer / createIframe职责与管理单例对象createSingle 进行拆分职责分离,符合单一职责原则

通过闭包的方式进行存储,不会多次创建同时只会在点击时创建,并且可复用

 

posted on 2021-06-28 22:27  smallpen  阅读(64)  评论(0)    收藏  举报