翼度科技»论坛 编程开发 JavaScript 查看内容

JavaScript中单例模式这样用

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
如果希望自己的代码更优雅、可维护性更高以及更简洁,往往离不开设计模式这一解决方案。
在JS设计模式中,最核心的思想:封装变化(将变与不变分离,确保变化的部分灵活,不变的部分稳定)。
单例模式

那么来说说第一个常见的设计模式:单例模式
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问方式,为了解决一个全局使用的类频繁被创建和销毁、占用内存的问题。
ES5中通过闭包

在ES5中,可以使用闭包(函数内部返回函数被外界变量所引用,导致这个函数里面的变量无法被释放,就构建成闭包)来保存这个类的实例。
  1. var Singeton = (function(){
  2.     var instance;
  3.    
  4.     function User(name,age){
  5.         this.name=name;
  6.         this.age=age;
  7.     }
  8.    
  9.     return function(name,age){
  10.         if(!instance){
  11.             instance = new User(name,age)
  12.         }
  13.         return instance
  14.     }
  15. })()
复制代码
此时这个实例一旦生成,每次都是返回这个实例,且不会被修改,可以看到下面的代码,当给 User 对象初始赋值 name:alice,age:18 时,以后再赋值便无效了,以及每次返回都是初始的实例对象。

ES6中使用类的静态属性

以上代码使用ES6语法来实现,通过类的静态属性来保存唯一的实例对象。
  1.   class Singeton {
  2.     constructor(name,age){
  3.         if(!Singeton.instance){
  4.             this.name = name;
  5.             this.age = age;
  6.             Singeton.instance = this;
  7.         }
  8.        return Singeton.instance;
  9.     }
  10. }
复制代码
创建方式仍然是一样的,通过 new 关键字创建类的实例对象。
案例

那这样一种设计模式在开发中实际有什么用途呢?我们试想这样一个业务场景:访问网站时,很久没有操作页面,此时授权过期,当我们点击页面上的任何一个地方,都会弹出一个登录框。
那么这个登录框,是全局唯一的,不会存在多份,也不会互相冲突,所以不需要每次都创建一份,保留初始那一份就够了。
提前创建节点

我们可能会想到首先在页面中提前创建节点,编写好页面样式,最后通过控制元素的 display 属性来达到显示和隐藏的效果。
  1. 登录对话框
  2. <button id="open">打开</button>
  3. <button id="close">关闭</button>
复制代码
这样可以完成需求,全局只有一个登录框,每次都展示同一个。但问题是dom元素从一开始它创建好并添加到body中,无论是否需要用到,如果有些场景不需要登陆,那么这里初始渲染就会浪费空间。
单例模式

那如果不需要初始渲染,仅当需要时才使用,并且每次都返回同一个实例的单例模式应该如何实现呢?
我们可以这样处理
  1. [/code]虽然上面的方式可以达到效果,但是创建对象和管理单例的逻辑都放在了对象内部,是有些混乱的。并且如果下次需要创建页面中唯一的 iframe,或者 script 标签,就得将以上函数照抄一遍。
  2. [size=3]通用单例[/size]
  3. 首先拆分函数逻辑,将执行创建对象的逻辑拿出来
  4. [code]const createLayer = function(){
  5.   let div = document.createElement("div")
  6.   div.innerHTML = "登录对话框"
  7.   div.className = "modal"
  8.   div.style.display = "none"
  9.   document.body.appendChild(div);
  10.   return div;
  11. }
  12. const Modal = (function(){
  13.   let instance = null
  14.   return function(){
  15.       if(!instance){
  16.           instance = createLayer()
  17.       }
  18.       return instance
  19.   }
  20. })()
复制代码
以上修改后代码逻辑就更为清晰,但此时还不支持通用化的创建别的组件,这时候我们想想如何将创建单例的方法进行一些优化,是否可以将单例需要执行的函数抽象化。
  1. const createSingle = (function(fn){
  2.     let instance;
  3.     return function(){
  4.         return instance || ( instance = fn.apply(this, arguments))
  5.     }
  6. })()
复制代码
这样改造后,如果存在创建 iframe 的方法,也可以直接使用。
  1. const createIframe = function() {
  2.   const iframe = document.createElement('iframe');
  3.   iframe.style.display = 'none';
  4.   document.body.appendChild(iframe);
  5.   return iframe
  6. }
  7. const singleIframe = createSingle(createIframe)
  8. document.querySelector("#open").onclick = function(){
  9. const iframe = singleIframe()
  10. iframe.style.display = 'block'
  11. }
复制代码
实际应用

以上都是咱小打小闹的试用,那再来看看社区中一些非常棒的实现吧~ 比如:React 中常用的状态管理工具 Redux 就使用到了单例模式,它有这样一些要求。

  • 单一数据源:整个应用的 state 只存在于唯一一个 store 中。
  • State 是只读的:不要直接改变 state 的值,唯一改变 state 的方法就是触发 action。
  • reducer 是纯函数:需要编写纯函数 reducer 来修改 state 的值。
来看看 Redux 的源码,为了便于阅读已删减部分逻辑判断和注释,可以看到通过 store 的 getState 方法每次获取闭包中的 currentState。

单例模式在内存中只有一个实例,可以减少内存开支,同时还能在系统设置全局的访问点,优化和共享资源。
以上就是单例模式的相关介绍。更多有关 前端、设计模式 的内容可以参考我其它的博文,持续更新中~

来源:https://www.cnblogs.com/vigourice/p/17165941.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具