JavaScript单例模式:从传统实现到惰性单例的演进

作者:纸飞机时间:2026-10-22 11:08:14浏览:660

本文深入解析JavaScript中单例模式的多种实现方式。从传统基于变量的全局访问点出发,探讨利用闭包优化代码结构,进而通过代理类分离对象创建与实例判断逻辑以遵循单一职责原则。最后介绍高度抽象的惰性单例模式,展示如何通用化生成唯一实例,并结合登录框场景说明其实际应用价值。

传统单例模式与核心思想

传统单例模式

其核心定义是确保一个类仅存在一个实例,并提供一个全局访问点供外部获取该实例。

实现单例的核心思想

主要逻辑是使用一个变量标记当前类是否已创建过对象。若已创建,后续获取实例时直接返回之前保存的对象。接下来使用JavaScript强行实现这一思路,代码示例如下:


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类的唯一对象,这种传统写法在JavaScript中并无实际意义,因为JS本身没有类的概念。这种强行套用传统单例思想的代码冗长且晦涩,阅读体验不佳。下面我们将使用JavaScript的闭包特性来实现单例,代码示例如下:


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

代理模式与单一职责原则

上述闭包实现虽然达成了单例效果,但代码耦合度依然较高。中的CreateDiv构造函数同时承担了创建对象、执行初始化init方法以及保证唯一性三项职责。这种职责不明确的代码不符合编程规范,我们需要将构建对象与判断返回现有对象或构造新对象的逻辑分离,以符合单一职责原则。解耦后的代码如下:


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);
  };
  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); //true

此时,中的CreateDiv构造函数仅负责对象的构建。至于判断是否返回现有实例还是构造新实例,这一逻辑被移交给了代理类ProxySingletonCreateDiv处理。这种职责分离的代码结构更加清晰合理。

高度抽象的惰性单例

最后展示一个高度抽象的单例模式代码,体现了惰性单例的核心精髓:


//单例模式抽象,分离创建对象的函数和判断对象是否已经创建
  var getSingle = function (fn) {
    var result;
    return function () {
      return result || ( result = fn.apply(this, arguments) );
    }
  };

形参fn是我们的构造函数,我们只要传入任何自己需要的构造函数,就能生成一个新的惰性单例。比如说传入创建一个女朋友的构造函数,并且调用getSingle(),就能生成一个新的女朋友。如果以后再调getSingle(),也只会返回刚才创建的那个女朋友。至于新女朋友——不存在的。

单例模式的常用场景

单例常用场景

当业务逻辑只需要生成一个唯一对象时,适合使用单例模式。例如页面登录框,全局通常只需存在一个登录弹窗。若不使用单例思想,每次显示登录框时都重新生成并显示,不仅浪费性能,还可能导致意外出现多个登录框的Bug。

以上分享了JavaScript实现单例模式的相关心得,感谢支持。

作者最新文章