前面的话

  单例方式是指保险二个类仅有1个实例,并提供八个造访它的大局访问点。
单例情势是1种常用的情势,有壹对对象往往只供给三个,比方线程池、全局缓存、浏览器中的window对象等。在javaScript开垦中,单例方式的用途一样不行常见。试想一下,单击登陆按键时,页面中会出现3个报到浮窗,而以此登陆浮窗是唯一的,无论单击多少次登陆按键,这些浮窗都只会被成立2回,那么那几个登入浮窗就适合用单例形式来创建

 

正规单例

  要兑现一个专门的工作的单例情势并不复杂,无非是用贰个变量来申明当前是否已经为有些类创设过对象,假若是,则在下2回拿走该类的实例时,直接再次回到此前创立的对象。代码如下:

var Singleton = function( name ){ 
  this.name = name; 
  this.instance = null;
};
Singleton.prototype.getName = function(){ 
  alert ( this.name );
};
Singleton.getInstance = function( name ){ 
  if ( !this.instance ){
    this.instance = new Singleton( name );
  }
  return this.instance;
};
var a = Singleton.getInstance( 'sven1' ); 
var b = Singleton.getInstance( 'sven2' );
alert ( a === b );    // true

  或者:

var Singleton = function( name ){ 
  this.name = name;
};
Singleton.prototype.getName = function(){ 
  alert ( this.name );
};
Singleton.getInstance = (
  function(){ 
    var instance = null;
    return function( name ){
      if ( !instance ){
        instance = new Singleton( name );
      }
    })();
  }
  return instance;

  通过Singleton.getInstance来获得Singleton类的唯一目的,这种措施相对简便易行,但有2个主题材料,就是扩张了那么些类的“不透明性”,Singleton类的使用者必须了然那是三个单例类,跟过去通过new
XXX的方式来博取对象差别,那里偏要使用Singleton.getInstance来收获对象

  即使曾经做到了2个单例格局的编排,但那段单例情势代码的实际意义并不大

 

晶莹剔透单例

  未来的靶子是贯彻三个“透明”的单例类,用户从那个类中创立对象时,能够像使用其余任何普通类同样。在上面包车型大巴例证中,将利用CreateDiv单例类,它的效应是担当在页面中开创唯壹的div节点,代码如下

  var CreateDiv = (function () {
    var instance;
    var CreateDiv = function (html) {
      if (instance) {
        return instance;
      }
      this.html = html;
      this.init();
      return instance = this;
    };
    CreateDiv.prototype.init = function () {
      var div = document.createElement('div');
      div.innerHTML = this.html;
      document.body.appendChild(div);
    };
    return CreateDiv;
  })();

  var a = new CreateDiv('sven1');
  var b = new CreateDiv('sven2');
  alert(a === b);    // true

  固然以后到位了几个晶莹剔透的单例类的编排,但它同样有部分毛病。为了把instance封装起来,使用了自实行的无名氏函数和闭包,并且让那么些佚名函数重回真正的Singleton构造方法,那扩展了壹部分主次的复杂度,阅读起来也不是很清爽

  下面的代码中,CreateDiv构造函数实际上担当了两件事情。第一是创建对象和实施起先化init方法,第2是承接保险唯有三个目的。那是壹种倒霉的做法,至少那么些构造函数看起来很想得到。若是某天须求使用这么些类,在页面中创制多如牛毛的div,即要让这一个类从单例类产生三个惯常的可发出四个实例的类,那必须得改写CreateDiv构造函数,把调控成立唯一目的的那1段去掉,这种修改会带来不须求的烦心

 

代办达成单例

  今后通过引入代理类的办法,来消除地点提到的主题材料。依旧选用方面包车型地铁代码,首先在CreateDiv构造函数中,把担负管理单例的代码移除出去,使它成为一个日常的创办div的类

  var CreateDiv = function (html) {
    this.html = html;
    this.init();
  };
  CreateDiv.prototype.init = 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;
    }
  })();
  var a = new ProxySingletonCreateDiv('sven1');
  var b = new ProxySingletonCreateDiv('sven2');
  alert(a === b);

  通过引进代理类的法门,同样达成了1个单例格局的编排,跟在此之前区别的是,今后把肩负管理单例的逻辑移到了代理类proxySingletonCreateDiv中。那样1来,CreateDiv就变成了二个一般的类,它跟proxySingletonCreateDiv组合起来能够到达单例格局的功力

 

惰性单例

  惰性单例指的是在必要的时候才成立对象实例。惰性单例是单例形式的严重性,那种手艺在实质上开垦中那些管用

  下边继续以登6框的事例来证实

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

  那种办法有几个标题,如若根本不必要开始展览登入操作,登入浮窗一初阶就被缔造好,很有望将白白浪费一些
DOM 节点

  今后改写一下代码,使用户点击登6按键的时候才初步创办该浮窗

<button id="loginBtn">登录</button>
<script>
    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';
    };
</script>  

  即使未来达到了惰性的目标,但失去了单例的效应。每便点击登陆开关时,都会创建多少个新的报到浮窗div

  可以用二个变量来决断是不是业已创办过登入浮窗,代码如下

    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;
        }
    })();
    document.getElementById( 'loginBtn' ).onclick = function(){
        var loginLayer = createLoginLayer();
        loginLayer.style.display = 'block';
    };

  上边的代码仍旧存在如下难点:

  1、违反单一职分规范的,成立对象和管制单例的逻辑都位居
createLoginLayer对象内部

  二、如若下次亟待创制页面中唯一的iframe,只怕script标签,用来跨域请求数据,就必须得画虎类犬,把createLoginLayer函数大概照抄三回

    var createIframe= (function(){
        var iframe;
        return function(){
            if ( !iframe){
                iframe= document.createElement( 'iframe' );
                iframe.style.display = 'none';
                document.body.appendChild( iframe);
            }
            return iframe;
        }
    })();

 

通用惰性单例

  今后内需把不改变的有的隔开分离出来,先不思念创设四个div和创建叁个iframe有稍许差距,管理单例的逻辑其实是截然能够抽象出来的,这些逻辑始终是同样的:用2个变量来注解是不是创制过对象,假若是,则在下次直接再次回到那个已经创办好的目的

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

  然后,把什么管理单例的逻辑从原本的代码中抽离出来,这么些逻辑被封装在getSingle函数内部,创制对象的法子fn被当成参数动态传入getSingle函数

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

  接下去将用来创建登六浮窗的不二等秘书籍用参数fn的花样传播getSingle,不仅能够流传createLoginLayer,还是能传播createScript、createIframe、createXhr等。之后再让getSingle再次回到一个新的函数,并且用三个变量result来保存fn的企图结果。result变量因为身在闭包中,它永世不会被灭绝。在前些天的呼吁中,要是result已经被赋值,那么它将赶回那个值

    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';
    };

  下边再试试创造唯1的iframe用于动态加载第3方页面

    var createSingleIframe = getSingle(function () {
      var iframe = document.createElement('iframe');
      document.body.appendChild(iframe);
      return iframe;
    });
    document.getElementById('loginBtn').onclick = function () {
      var loginLayer = createSingleIframe();
      loginLayer.src = 'https://www.hao123.com';
    };

  上边的事例中,创造实例对象的天职和治本单例的天职分别放置在三个章程里,那八个章程能够独自变化而互不影响,当它们连接在共同的时候,就成功了创设唯壹实例对象的效益

  那种单例情势的用途远不止创建对象,比方一般渲染完页面中的八个列表之后,接下去要给那一个列表绑定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 getSingle = function (fn) {
        var result;
        return function () {
            return result || (result = fn.apply(this, arguments));
        }
    };
    var bindEvent = getSingle(function(){
        document.getElementById( 'div1' ).onclick = function(){
            alert ( 'click' );
        }
        return true;
    });
    var render = function(){
        console.log( '开始渲染列表' );
        bindEvent();
    };
    render();
    render();
    render();

  可以看来,render函数和bind伊芙nt函数都各自实行了一次,但div实际上只被绑定了3个风云

 

相关文章

网站地图xml地图