眼前的言辞

  单例模式是乘保证一个近似就发生一个实例,并提供一个拜访它的全局访问点。
单例模式是同等种常用之模式,有局部靶往往只需要一个,比如线程池、全局缓存、浏览器被的window对象等。在javaScript开发中,单例模式之用同样好广泛。试想一下,单击登录按钮时,页面被见面冒出一个记名浮窗,而之登录浮窗是绝无仅有的,无论单击多少坏登录按钮,这个浮窗都仅仅会于创造同不好,那么这个登录浮窗就可用单例模式来创造

 

规范单例

  要落实一个规范的单例模式并无复杂,无非是故一个变量来表明当前是不是就为有类创建了对象,如果是,则在生一样差拿走该类的实例时,直接回之前创建的目标。代码如下:

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类的绝无仅有目标,这种方法相对简单,但发生一个题材,就是充实了此类似的“不透明性”,Singleton类的使用者要明白这是一个单例类,跟过去通过new
XXX的计来得到对象不同,这里偏要使用Singleton.getInstance来获得对象

  虽然已经完成了一个单例模式之编纂,但当时段单例模式代码的实际意义并无要命

 

晶莹剔透单例

  现在底靶子是实现一个“透明”的单例类,用户从之近乎吃开创目标时,可以像下外任何普通类一样。在下面的事例中,将使用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方法,第二凡是确保单独发一个目标。这是一模一样栽不好的做法,至少是构造函数看起颇奇怪。假设某天需要以这看似,在页面中创造千千万万的div,即如果被这仿佛从单例类变成一个一般的可是发出多独实例的类似,那必得改变写CreateDiv构造函数,把控制创建唯一目标的那无异段去丢,这种修改会带动不必要之烦乱

 

代理实现单例

  现在由此引入代理类的法门,来缓解者提到的题材。依然采用方面的代码,首先以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);

  通过引入代理类的不二法门,同样好了一个单例模式的修,跟之前不同之凡,现在将负责管理单例的逻辑移到了代理类proxySingletonCreateDiv中。这样一来,CreateDiv就改成了一个日常的切近,它同proxySingletonCreateDiv组合起来可高达单例模式的机能

 

惰性单例

  惰性单例指的是于待之早晚才创建对象实例。惰性单例是单例模式之要,这种技能于实际支出被非常管用

  下面继续为登录框的事例来验证

<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 节点

  现在改写一下代码,使用户点击登录按钮的上才开创造该浮窗

<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对象中

  2、如果下次待创造页面被唯一的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有稍许差异,管理单例的逻辑其实是一心可抽象出的,这个逻辑始终是同一的:用一个变量来表明是否创造了对象,如果是,则以下次直返回这个曾创办好的靶子

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

  下面再试试创建唯一的iframe用于动态加载第三方页面

    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函数和bindEvent函数都各自施行了3次于,但div实际上仅给绑定了一个轩然大波

 

相关文章

网站地图xml地图