JavaScript设计模式之单例模式应用场景案例详解

 更新时间:2023年05月29日 11:07:33   作者:一颗冰淇淋  
这篇文章主要为大家介绍了JavaScript中单例模式的应用场景案例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

单例模式

如果希望自己的代码更优雅、可维护性更高以及更简洁,往往离不开设计模式这一解决方案。

在JS设计模式中,最核心的思想:封装变化(将变与不变分离,确保变化的部分灵活,不变的部分稳定)。

那么来说说第一个常见的设计模式:单例模式

单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问方式,为了解决一个全局使用的类频繁被创建和销毁、占用内存的问题。

ES5中通过闭包

在ES5中,可以使用闭包(函数内部返回函数被外界变量所引用,导致这个函数里面的变量无法被释放,就构建成闭包)来保存这个类的实例。

var Singeton = (function(){
    var instance;
    function User(name,age){
        this.name=name;
        this.age=age;
    }
    return function(name,age){
        if(!instance){
            instance = new User(name,age)
        }
        return instance
    }
})()

此时这个实例一旦生成,每次都是返回这个实例,且不会被修改,可以看到下面的代码,当给 User 对象初始赋值 name:alice,age:18 时,以后再赋值便无效了,以及每次返回都是初始的实例对象。

ES6中使用类的静态属性

以上代码使用ES6语法来实现,通过类的静态属性来保存唯一的实例对象。

class Singeton {
    constructor(name,age){
        if(!Singeton.instance){
            this.name = name;
            this.age = age;
            Singeton.instance = this;
        }
       return Singeton.instance;
    }
}

创建方式仍然是一样的,通过 new 关键字创建类的实例对象。

案例

那这样一种设计模式在开发中实际有什么用途呢?我们试想这样一个业务场景:访问网站时,很久没有操作页面,此时授权过期,当我们点击页面上的任何一个地方,都会弹出一个登录框。

那么这个登录框,是全局唯一的,不会存在多份,也不会互相冲突,所以不需要每次都创建一份,保留初始那一份就够了。

提前创建节点

我们可能会想到首先在页面中提前创建节点,编写好页面样式,最后通过控制元素的 display 属性来达到显示和隐藏的效果。

<div class="modal">登录对话框</div>
<button id="open">打开</button>
<button id="close">关闭</button>
<style>
  .modal {
    display: none;
    /* 其他布局代码省略 */
  }
</style>
<script>
  document.querySelector("#open").onclick = function(){
     const modal = document.querySelector('.modal')
     modal.style.display = 'block'
  }
</script>

这样可以完成需求,全局只有一个登录框,每次都展示同一个。但问题是dom元素从一开始它创建好并添加到body中,无论是否需要用到,如果有些场景不需要登陆,那么这里初始渲染就会浪费空间。

单例模式

那如果不需要初始渲染,仅当需要时才使用,并且每次都返回同一个实例的单例模式应该如何实现呢?

我们可以这样处理

<!-- 去除class为modal的标签,动态创建 -->
<script>
const Modal = (function(){
  let instance = null
  return function(){
      if(!instance){
          instance = document.createElement("div")
          instance.innerHTML = "登录对话框"
          instance.className = "modal"
          instance.style.display = "none"
          document.body.appendChild(instance)
      }
      return instance
  }
})()
document.querySelector("#open").onclick = function(){
      //创建modal,如果放在外面,一开始就会创建元素
      const modal = Modal()    
      //显示modal
      modal.style.display = "block"
  }
  document.querySelector("#close").onclick = function(){
      const modal = Modal()    
      modal.style.display = "none"
  }
</script>

虽然上面的方式可以达到效果,但是创建对象和管理单例的逻辑都放在了对象内部,是有些混乱的。并且如果下次需要创建页面中唯一的 iframe,或者 script 标签,就得将以上函数照抄一遍。

通用单例

首先拆分函数逻辑,将执行创建对象的逻辑拿出来

const createLayer = function(){
  let div = document.createElement("div")
  div.innerHTML = "登录对话框"
  div.className = "modal"
  div.style.display = "none"
  document.body.appendChild(div);
  return div;
}
const Modal = (function(){
  let instance = null
  return function(){
      if(!instance){
          instance = createLayer()
      }
      return instance
  }
})()

以上修改后代码逻辑就更为清晰,但此时还不支持通用化的创建别的组件,这时候我们想想如何将创建单例的方法进行一些优化,是否可以将单例需要执行的函数抽象化。

const createSingle = (function(fn){
    let instance; 
    return function(){
        return instance || ( instance = fn.apply(this, arguments))
    }
})()

这样改造后,如果存在创建 iframe 的方法,也可以直接使用。

const createIframe = function() {
  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  document.body.appendChild(iframe);
  return iframe
}
const singleIframe = createSingle(createIframe)
document.querySelector("#open").onclick = function(){
 const iframe = singleIframe()
 iframe.style.display = 'block'
}

实际应用

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

  • 单一数据源:整个应用的 state 只存在于唯一一个 store 中。
  • State 是只读的:不要直接改变 state 的值,唯一改变 state 的方法就是触发 action。
  • reducer 是纯函数:需要编写纯函数 reducer 来修改 state 的值。

来看看 Redux 的源码,为了便于阅读已删减部分逻辑判断和注释,可以看到通过 store 的 getState 方法每次获取闭包中的 currentState。

单例模式在内存中只有一个实例,可以减少内存开支,同时还能在系统设置全局的访问点,优化和共享资源。

以上就是单例模式的相关介绍。更多有关 前端、设计模式 的内容请关注脚本之家其它相关文章!

相关文章

  • 微信小程序 视图层(xx.xml)和逻辑层(xx.js)详细介绍

    微信小程序 视图层(xx.xml)和逻辑层(xx.js)详细介绍

    这篇文章主要介绍了微信小程序 视图层(xx.xml)和逻辑层(xx.js)详细介绍的相关资料,需要的朋友可以参考下
    2016-10-10
  • 微信小程序 Toast自定义实例详解

    微信小程序 Toast自定义实例详解

    这篇文章主要介绍了微信小程序 Toast自定义实例详解的相关资料,需要的朋友可以参考下
    2017-01-01
  • Web Animations API实现一个精确计时的时钟示例

    Web Animations API实现一个精确计时的时钟示例

    这篇文章主要为大家介绍了Web Animations API实现一个精确计时的时钟示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • JavaScript 中的文档对象模型 DOM

    JavaScript 中的文档对象模型 DOM

    DOM,即文档对象模型,前端开发工程师必学的基础知识,在本文将介绍如何在 HTML 文档中选择元素、如何创建元素、如何更改内联 CSS 样式以及如何监听事件,需要的朋友可以参考一下
    2021-10-10
  • Thinkphp5微信小程序获取用户信息接口的实例详解

    Thinkphp5微信小程序获取用户信息接口的实例详解

    这篇文章主要介绍了Thinkphp5微信小程序获取用户信息接口的实例详解的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下
    2017-09-09
  • 微信小程序 Storage API实例详解

    微信小程序 Storage API实例详解

    这篇文章主要介绍了微信小程序 Storage API实例详解的相关资料,需要的朋友可以参考下
    2016-10-10
  • 微信小程序 欢迎页面的制作(源码下载)

    微信小程序 欢迎页面的制作(源码下载)

    这篇文章主要介绍了微信小程序 欢迎页面的制作含(源码下载)的相关资料,这里实现欢迎页面,开始做应用的时候都会用到,需要的朋友可以参考下
    2017-01-01
  • Google 地图获取API Key详细教程

    Google 地图获取API Key详细教程

    本文主要介绍Google 地图API Key,开发Google 地图应用的朋友都知道,在开发的前需要免费的Google 地图API Key,这里详细给出获得API Key的流程,有需要的小伙伴参考下
    2016-08-08
  • JavaScript事件委托原理

    JavaScript事件委托原理

    这篇文章主要介绍了JavaScript事件委托原理, 事件委托也称为事件代理。就是利用事件冒泡,把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托就无法实现,下面我们一起来学习文章的具体详细内容吧
    2021-12-12
  • Promise改写获取萤石云直播地址接口示例

    Promise改写获取萤石云直播地址接口示例

    这篇文章主要为大家介绍了Promise改写获取萤石云直播地址接口示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08

最新评论