JavaScript单例模式的四种实现方式详解
本文详细解析JavaScript中单例模式的四种基本形式:基础JSON对象、闭包局部变量、惰性加载及分支判断。通过对比各模式的优缺点,如内存占用与性能权衡,帮助开发者根据实际需求选择最合适的单例实现方案。
单例模式概述
本文详细介绍JavaScript设计模式中的单例模式,并提供四种基本形式的实现示例,供读者参考学习。
单例模式(Singleton Pattern),亦称单体模式,其核心定义如下:
1. 命名空间组织:单例模式可用于创建命名空间,将一系列相关的属性和方法整合为一个逻辑单元,从而减少全局变量的使用。该逻辑单元内的代码通过唯一的变量进行访问。
2. 三大特征: ① 确保该类仅存在一个实例; ② 类内部自行创建该实例,即由类自身负责实例化对象; ③ 向整个系统公开访问该实例的接口。
单例模式的四种基本形式
3. 单例模式包含四种基本实现形式:
第一种:基础JSON对象
第一种是最简单的单例,仅被实例化一次,可简记为JSON对象形式。
(1)基本结构
var userInfo={//已经自行被实例化 其实是一json对象
name:"测试名称",
dept:"测试PD",
code:"测试PD001",
getName:function () {
return "测试"
}
};
(2)使用方式与JSON对象一致:通过点号 " . " 进行属性或方法访问。
alert(userInfo.getName())
以下是单例模式用于划分命名空间,并将相关属性和方法组织在一起的简要介绍:
var comm={};//一个空对象
comm.userInfo={//空对象下的第一个命名空间
name:"命名空间1下的",
code:"001"
}
comm.funcInfo={//空对象下的第二个命名空间
funcName:"命名空间2下的",
code:"002"
}
总结:该方式显示对象的变量值并非动态加载,且对象未进行显式初始化,由此引出了第二种单例模式。
第二种:包含局部变量的单例
第二种:包含局部变量的单例模式
需求:模拟通过Ajax从数据库加载数据的过程。
(1)简单模拟Ajax操作流程
//模拟一个Ajax操作
function Ajax() {};//空对象
//静态函数 模拟作为从数据库取值 此处值写死的
Ajax.request=function (url,fn) {
//默认永远回调成功
if(true){
fn("测试值1","测试值2")
}
}
(2)针对第一种模式中数据非动态加载且未显式实例化的问题,此处利用闭包原理进行解决。
//使用闭包的原理解决:动态从数据库加载数据 ,显示实例化
var userInfo=(function () {
//(1)利用闭包使单体有自己的私有局部变量
var name="";
var code="";
//(2)利用ajax访问数据库取到数据
Ajax.request("url",function (n,c) {//由于模拟的ajax中只是简单传递参数,所以第一个参数可以任意
name=n;
code=c;
})
//(3)单体实现私有变量的赋值
return {
name:name,
code:code
}
})()
(3)使用此种方式的单例,无需手动实例化,直接返回一个单例对象即可(因为在调用userInfo时,函数直接return返回了一个单例)。
alert(userInfo.name);
总结:
(1)优点:具有较高的灵活性。
(2)缺点:当return的单体数据量较大时,每次加载都需从数据库获取数据并执行,会影响程序性能。由于该方式在每次加载时均直接执行,若return单体数据量大,将影响呈现性能,由此引出第三种单例模式。
第三种:惰性单例
第三种:惰性单例。其解决方案为:仅在调用方法时才实例化单例,而非在加载时立即执行。
因此,在第二种模式的基础上进行修改,实现如下:
(1)模拟Ajax从数据库加载数据的逻辑保持不变。
//模拟一个Ajax操作
function Ajax() {}
//静态函数 模拟作为从数据库取值
Ajax.request=function (url,fn) {
//默认永远回调成功
if(true){
fn("测试值1","测试值2")
}
}
(2)实现动态从数据库加载数据并显式实例化:使用一个函数(Init())封装产生单体的逻辑,并通过一个私有变量来返回该函数(Init())。
//使用闭包的原理解决:动态从数据库加载数据 ,显示实例化
var UserInfo=(function () {
var userInfo="";//私有变量
function Init() {//在产生单体方式为包裹一层初始化函数
//利用闭包使单体有自己的私有局部变量
var name="";
var code="";
//利用ajax访问数据库取到数据
Ajax.request("url",function (n,c) {
name=n;
code=c;
})
//单体
return {
name:name,
code:code,
}
}
return {//此时开始调用初始化函数实现单体的产生
getInstance:function () {
if(userInfo){//userInfo=""为false
return userInfo;
}else {
userInfo=Init();
return userInfo;
}
}
}
})()
(3)通过访问UserInfo对象内部提供的获取初始化对象的函数(getInstance())来获取结果。
alert(UserInfo.getInstance().name);
总结:惰性单例的实质是通过对产生单体的函数进行再次封装(使用函数封装),再通过该类提供的唯一接口(getInstance()方法)访问初始化单体的函数。
第四种:分支单例
第四种:分支单例
简单应用场景:在Ajax请求中,根据不同浏览器获取不同的XHR对象(将浏览器间的差异封装到动态方法中,适用于解决浏览器兼容性问题)。
例如以下简单示例:在不同电脑分辨率下初始化不同的界面(此处仅以弹窗显示为例)。
(1)获取电脑屏幕分辨率
//得到机器的分辨率
var screenWidth=window.screen.width;//width
var screenHeight=window.screen.height;//height
(2)进行分支判断处理,将差异封装到动态方法中
var portalInfo=(function () {
//单体
var $1280_1024={info:'1,2,3,5'}//单体1
var $1366_768={info:'4,2,1,2'}//单体2
//动态图选择浏览器的差异结果(这里是分辨率)
if(screenWidth==1280){
return $1280_1024;//返回单体进行初始化
}else if(screenWidth==1366){
return $1366_768;//返回单体进行初始化
}else {
throw new Error("请检查你当前的电脑分辨率")
}
})();
(3)使用代码获取最终结果
alert(portalInfo.info)//我的结果为4,2,1,2 这是由于我的电脑的分辨率为1366*768
总结:分支单例存在一个缺点:在分支中,单体1和单体2均被创建并保存在内存中,但实际只使用其中一个。因此需要在计算时间与占用内存之间进行取舍。
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码的运行效果。
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述的JavaScript程序设计内容对大家有所帮助。
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
