当前位置: 首页 > 科技观察

设计模式-作品中使用的单例模式

时间:2023-03-18 16:41:49 科技观察

转载本文请联系DYBOY公众号。单例模式是一种非常常用的设计模式。在我们的工作中,我们会用到弹窗、音频管理、消息管理中心、公共实用函数类等在应用程序中只需要一个实例的场景。这些都类似于单例模式形影不离。单例模式很常见也很重要。将单例模式应用到程序开发设计中,可以减少重复代码,提高程序效率。同时,单例的唯一性也使得数据流向更加清晰,便于维护和管理。1、什么是单例模式单例模式(SingletonPattern)保证一个类只有一个实例,并提供一个全局访问点来访问它。是不是马上就想到了JavaScript中的全局变量window和localStorage,它们全局提供访问点,只有唯一的实例。在一些多线程编程语言中,单例模式涉及到同步锁的问题,而JavaScript是单线程运行的,所以线程安全问题可以暂时忽略。单例类似于单例。直到程序被销毁,整个程序中再没有第二个能与之匹敌。2.实现单例模式单例模式从定义上可以看出,是一种比较简单的设计模式,其核心思想是保证实例唯一,所以简单实现一个屏蔽函数单例类如下,并逐步完善步。2.1目前maskedsingletonclass的工作环境是基于ES6及以上的开发模型,所以我们直接引入类的思想来实现。如果同学们需要了解相关的基础内容,推荐阅读:《这些JS设计模式的基础知识点你都会了吗?》。遮罩层的作用:最大的一层,覆盖浏览器可见区域,防止用户的所有点击事件透明遮罩层因此,遮罩层单例的实现如下:/***遮罩遮罩层单例*/classMask{staticinstance:Mask;privateisShow:boolean;privatemaskDom:HTMLDivElement;staticgetInstance(){if(!Mask.instance){Mask.instance=newMask();}returnMask.instance;}constructor(){this.isShow=false;this.maskDom=this.init();}/***创建一个蒙版DOM*/privateinit(){constdom=document.createElement("div");dom.setAttribute("style","z-index:99999;position:fixed;top:0;left:0;width:100%;height:100%;display:none;pointer-events:all;user-select:none;cursor:not-allowed;");document.body.appendChild(dom);returndom;}/***显示显示掩码*/publicshow(){if(this.isShow)return;this.maskDom.style["display"]="block";this.isShow=true;}/***hide隐藏掩码*/publichide(){if(!this.isShow)return;this.maskDom.style["display"]="none";this.isShow=false;}}//直接导出实例exportdefaultMask。获取实例();用法:importMaskfrom"./utils/Mask";Mask.show();Mask.hide();This一种在一开始就创建实例的方式称为“饿汉单例”,另一种在需要时才创建实例的方式称为“懒汉单例”,所以“饿汉单例”的缺点是先初始化加载类时,浪费内存。但是在现代,借助Webpack等打包构建工具,如果不用这个组件,这个组件是不会被打包的。此外,在按需加载组件的React和Vue框架的设计和实现下,组件也是通过网络按需下载和分包的。然后缓存组件文件,因此浪费内存的缺点可以忽略不计。因此,在JavaScript中并没有太大的懒惰和饥饿的区别。建议在导出的时候导出一个实例,例如:exportdefaultMask.getInstance();2.2TransparentMaskSingleton在上面的导出实例中,只能调用Mask.getInstance(),已经避免了其他用户如何判断是否是单例类的问题。另外,如果我们的导出形式是这样的:exportdefaultMask;那么其他同学在引用这个组件的时候,如果不知道这是一个单例类,那么他可能会直接在newMask()方法中使用,然后创建多个不同的实例就失去了单例的效果。为了使单例类使用与普通类相同的写对象方式,我们将这种处理方式与其他普通类一样,称为“透明”。为此,稍微改变一下构造函数中的判断:constructor(){if(Mask.instance){returnMask.instance;}this.isShow=false;this.maskDom=this.init();returnMask.instance=this;}...导出默认掩码;使用验证:consta=newMask();a.show();constb=newMask();b.hide();console.log("是否相等:",a===b);//输出:是否等于true,掩码隐藏。为了保证类的透明性和使用的统一性,可以通过在构造函数中预先判断是否有实例来实现。2.3单例工具功能通过上面单例模式的实现和使用,可以想到一个问题。单例模式只需要保证唯一实例即可,保证唯一实例的方式是通过一个变量判断当前实例是否已经创建,如果已经创建则直接返回实例,否则返回之后的实例创建。那么一般的单例模式应该把单例和类的职责分开。说起来和之前分享的《从“图片预加载”认识代理设计模式》中的缓存代理很像。借助代理模式的思想,使用闭包来缓存单例。单例缓存工具函数:exportconstproxySingleton=(fn)=>{letinstance;return()=>{if(instance)returninstance;returninstance=newfn();};};传入我们之前写的任意一个类,类名指向构造函数exportconstsingletonMask=proxySingleton(Mask);使用时:import{singletonMask}from"./utils/ProxySingleton";consta=singletonMask();a.show();constb=singletonMask();b.hide();console.log("是否相等:",a===b);//output:是否等于true,mask隐藏所以我们拆分了单例和创建mask类的职责,遵循单一职责原则。2.4ES6exportinstanceimport一个单例举一反三,在ES6中直接export一个instance是不是可以算作单例?exportdefaultnewMask();importimport采用单例模式,多次使用import在同一个模块中,只会引入该模块的一个实例——《ECMAScript 6 入门》因此,如果我们想要一个普通的单例对象,我们可以export导出时直接实例化。我们之前研究过EventEmitter3事件触发的原理。为了让它全局只有一个实例,我们使用如下://./utils/eventEmitter.tsimportEventEmitterfrom'eventemitter3';exportdefaultnewEventEmitter();导出的eventEmitter是一个单一的实例,唯一的,全局可访问的。3.总结单例模式在工作中经常用到。当我们有意识地使用单例来管理具有独特属性的实例时,将使程序更易于管理和维护。结合ES6的import和export关键字,单例模式的应用变得更加简单。在实现单例的时候,我们把单例和掩码类的功能分开了,也有的结合在一起,看你在项目中想怎么设计。如果单例不应用于大型组件,其实建议将它们组合在一起,以帮助将整个功能类维护在一个文件中。

猜你喜欢