JavaScript单例模式:从传统实现到惰性单例的演进
本文深入解析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实现单例模式的相关心得,感谢支持。
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
