JavaScript单例模式的四种实现方式详解

作者:拾光时间:2026-10-23 18:41:18浏览:423

本文详细解析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程序设计内容对大家有所帮助。

作者最新文章