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

在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。

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

以上就是单例模式的相关介绍。更多有关 前端设计模式 的内容可以参考我其它的博文,持续更新中~

JavaScript中单例模式这样用的更多相关文章

  1. JavaScript设计模式-单例模式、模块模式(转载 学习中。。。。)

    (转载地址:http://technicolor.iteye.com/blog/1409656) 之前在<JavaScript小特性-面向对象>里面介绍过JavaScript面向对象的特性 ...

  2. JavaScript中的单例模式

    单例模式 在JavaScript中,单例(Singleton)模式是最基本又最有用的模式之一.这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一的变量进行访问.确保单例 ...

  3. JavaScript设计模式 - 单例模式

    单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 一.实现一个标准的单例模式,用一个变量来标志当前是否已经为某个类创建过对象, 如果是,则在下一次获取该对象实例时,直接返回之前创建的对 ...

  4. JavaScript 中常见设计模式整理

    开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式.本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知. JavaScript 中常见设计模 ...

  5. JavaScript中常见的十五种设计模式

    在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”. 在JavaScript中并没有类这种概念,JS中的函数属于一等对象,在JS中定义一个对象非常简单(var obj = {}), ...

  6. [读书笔记] JavaScript设计模式: 单例模式

    单例模式:保证一个类只有一个实例,并提供一个可以访问它的全局访问点. 一种简单.方便的写法就是用一个变量来标识当前类是否已经创建过对象,如果有,则返回已经创建好的对象,否则创建一个新对象,并将其返回. ...

  7. javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈

    Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...

  8. javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...

  9. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  10. javascript中的操作符详解1

    好久没有写点什么了,根据博主的技术,仍然写一点javascript新手入门文章,接下来我们一起来探讨javascript的操作符. 一.前言 javascript中有许多操作符,但是许多初学者并不理解 ...

随机推荐

  1. 2023-02-28:moonfdd/ffmpeg-go是用go语言绑定ffmpeg的库,目前是github上最好用的库。请用go语言将yuv文件编码为h264文件。

    2023-02-28:moonfdd/ffmpeg-go是用go语言绑定ffmpeg的库,目前是github上最好用的库.请用go语言将yuv文件编码为h264文件. 答案2023-02-28: 使用 ...

  2. 2020-09-29:介绍volatile功能。

    福哥答案2020-09-29:#福大大架构师每日一题# 功能如下:1.内存可见.2.禁止指令重排序. 实现如下:1.字节码层面 ACC_VOLATILE 2.JVM层面volatile内存区的读写 都 ...

  3. 2022-02-20:设计内存文件系统。 设计一个内存文件系统,模拟以下功能: ls: 以字符串的格式输入一个路径。如果它是一个文件的路径,那么函数返回一个列表,仅包含这个文件的名字。如果它是一个文件

    2022-02-20:设计内存文件系统. 设计一个内存文件系统,模拟以下功能: ls: 以字符串的格式输入一个路径.如果它是一个文件的路径,那么函数返回一个列表,仅包含这个文件的名字.如果它是一个文件 ...

  4. 2021-05-18:Nim博弈。给定一个正数数组arr,先手和后手每次可以选择在一个位置拿走若干值, 值要大于0,但是要小于该处的剩余。谁最先拿空arr,谁赢。根据arr,返回谁赢 。

    2021-05-18:Nim博弈.给定一个正数数组arr,先手和后手每次可以选择在一个位置拿走若干值, 值要大于0,但是要小于该处的剩余.谁最先拿空arr,谁赢.根据arr,返回谁赢 . 福大大 答案 ...

  5. GPT大语言模型Alpaca-lora本地化部署实践【大语言模型实践一】

    模型介绍 Alpaca模型是斯坦福大学研发的LLM(Large Language Model,大语言)开源模型,是一个在52K指令上从LLaMA 7B(Meta公司开源的7B)模型微调而来,具有70亿 ...

  6. PHP反序列化常用魔术方法

    PHP反序列化 php序列化(serialize):是将变量转换为可保存或传输的字符串的过程 php反序列化(unserialize):就是在适当的时候把这个字符串再转化成原来的变量使用 PHP反序列 ...

  7. v8 setup

    记录下笔者本人搭建v8环境的过程 环境:处于一些原因笔者选择在kali2023上搭建v8,kali上可以搭建成功但是调试脚本加载有问题,fuck kali,还是ubuntu好,笔者使用了ubuntu2 ...

  8. hvv面试常见框架漏洞问题合集

    1.thinkphp 特征判断 直接在url后加/?s=1 whatweb进行探测,方式:whatweb URL 漏洞 5.0 RCE 原理 thinkphp底层没有对控制器名进行很好的合法性校验,导 ...

  9. const 使用

    宏定义与const的区别?(概念题是最容易丢分)1. 发生时机不一样: 宏定义发生在预处理时,const关键字发生编译时2. 宏定义仅仅只做了字符串的替换,没有类型检查; const关键字有类型检查, ...

  10. 手动删除了Linux下syslog--/var/log/messages怎么办?

    引言 Linux小白很容易犯得一个错误就是:查看日志的时候,尤其是系统日志,由于日志太多,把系统日志手动删除了.也就是把/var/log/messages文件删除了,而不是删除文件的内容.直接删除文件 ...