js单例模式

惰性单例

惰性单例指的是在需要的时候才创建对象实例。我们先来看一个例子,假如需求是一个唯一的登录窗口,第一种解决方案是在页面加载完成的时候便创建好这个div浮窗,这个浮窗肯定一开始是隐藏状态的
具体代码可以参考single-case.js

   var loginLayer = (function(){
      var div = document.createElement('div');
      div.innerHTML = '我是登录浮窗';
      div.style.display = 'none';
      // get body ref
      document.body.appendChild(div);
      return div;
   })()
   document.getElementById('loginBtn').onclick = function(){
       loginLayer.style.display = 'block';
   }

这样做一个问题,很多游客并不想登录,如果登录浮窗总是一开始就被创建好,那么很有可能白白浪费一些Dom节点,现在改写一下使用户点击登录按钮的时候才开始创建浮窗

   var createLoginLayer = function(){
      var div = document.createElement('div');
      div.innerHTML = '我是登录浮窗';
      div.style.display = 'none';
      document.body.appendChild(div);
      return div;
   }
   document.getElementById('loginBtn').onclick = function(){
       var loginLayer = createLoginLayer();
       loginLayer.style.display = 'block';
   }

虽然现在达到了惰性的目的,但失去了单例的效果。我们可以使用一个变量来判断是否已经创建过登录浮窗

   var createLoginLayer = (function(){
       //使用闭包来保存这个变量
       var div;
       return function(){
           if(!div){
               div = document.createElement('div');
               div.innerHTML = '我是登录浮窗';
               div.style.display = 'none';
               document.body.appendChild(div);
           }
           return div;
       }
   })()

通用的惰性单例

上面的代码还是有问题,创建对象和管理单例的逻辑都放在createLoginLayer对象内部。如果我们想再创建页面中唯一的一个iframe或者script标签,就要把刚才的创建函数几乎照抄一遍
我们需要把不变的逻辑抽离出来

   var obj;
   if(!obj){
      obj = xxx;
   }

现在我们就把如何管理单例的逻辑从原来的代码中抽离出来

   var getSingle = function(fn){
      var result;
      return function(){
          return result || (result = fn.apply(this, arguments))
      }
   }

接下来将创建浮窗的方法用参数fn的形式传入getSingle

  var createLoginLayer = function(){
      var div = document.createElement('div');
      div.innerHTML = '我是登录浮窗';
      div.style.display = 'none';
      document.body.appendChild(div);
      return div;
  } 
  //变量保存着获取单例变量的函数
  var createSingleLoginLayer = getSingle(createLoginLayer);
  document.getElementById('loginBtn').onclick = function(){
       var loginLayer = createSingleLoginLayer();
       loginLayer.style.display = 'block';
   }

这种单例模式的作用远不止创建对象,比如我们通常渲染完页面中的一个列表之后,接下来要给这个列表绑定click事件,如果是通过ajax动态往列表中追加数据,在使用事件代理的前提下,click事件实际上只需要在第一次渲染列表的时候绑定一次,但是我们不想去判断当前是否是第一次渲染列表,如果借助于jQuery,我们通常选择给节点绑定one事件

   var bindEvent = function(){
      $('div').one('click', function(){
         alert('click');
      });
   };
   var render = function(){
      console.log('开始渲染列表');
      bindEvent();
   }
   render();
   render();
   render();

如果使用getSingle函数,也能达到一样的效果

   var bindEvent = getSingle(function(){
       document.getElementById('div1').onclick = function(){
           alert('click');
       }
       return true;
   })
   var render = function(){
      console.log('开始渲染列表');
      bindEvent();
   }
   render();
   render();
   render();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值