前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >javascript设计模式-单例模式

javascript设计模式-单例模式

作者头像
FE情报局
发布2022-12-05 10:23:02
2640
发布2022-12-05 10:23:02
举报
文章被收录于专栏:FE情报局FE情报局

从今天开始,我将连续更新javascript的设计模式,资料来源主要是https://www.patterns.dev/ 这里涵盖了所有的设计模式,主要内容来自对这个网站的翻译加上自己的理解,也是自我学习的过程

前沿

设计模式是软件开发的基本组成部分,因为在大型的软件设计过程中,经常会出现一些设计模式,它可以用来优化我们一些代码逻辑和处理方式

过去的一段时间中,web整个开发体系发生了迅猛的变化,虽然一些设计模式可能已经没有了对应的价值,但是也有其中的一些设计模式已经发展到了解决一些现代问题

react相信大家都比较熟悉,在最近一段时间内获得了巨大的关注,哦不对,一直以来react都备受关注,从npm包的下载量就能很明显的看出来,react一直稳居几大框架榜首

也由于react的流行,传统的设计模式已经被修改优化,并且创建了新的设计模式,以便于在现代web开发中提供对应的价值,比如react的hook特性,可以替代很多传统设计模式,所以才有了这个专题,希望能够通过一起学习将设计模式的实现、好处、缺陷以及面试统统拿下

单例模式

单例表示的是可以实例化一次的类,并且可以全局访问。这个单一的实例可以在我们的应用程序中共享,所以单例模式非常适合管理应用程序中的全局状态

我们看一下单例到底是一个什么样的内容,我们可以构建一个Counter类,它有以下方法

  • 返回实例(getInstance)
  • 返回计数器当前的值(getCount)
  • count值加一(increment)
  • count值减一(decrement)
代码语言:javascript
复制
let counter = 0

class Counter {
  getInstance() {
    return this
  }
  
  getCount() {
    return counter
  }
  
  increment() {
    return ++counter
  }

  decrement() {
    return --counter
  }
}

虽然上述的功能都实现了,但是这个类实际上是不符合标准的,标准规定的是这个类只能实例化一次,但是上面的代码我们可以创建很多Counter实例

代码语言:javascript
复制
const counter1 = new Counter()
const counter2 = new Counter()

console.log(counter1.getInstance() == counter2.getInstance()) // false

通过两次实例化的方式之后,获取到的实例并不相等,两个实例只是不同实例的引用

只能一个实例

确保我们只能创建一个实例的办法是创建一个名为instance的变量,在构造函数中,我们可以在创建实例的时候将实例设置为对实例的引用,然后检查instantce变量是否已经有值来防止重复实例化,如果已经实例化,则抛出错误让用户知道已经存在了实例

代码语言:javascript
复制
let counter = 0
let instance = null

class Counter {
  constructor() {
    if(instance) {
      throw new Error("只能有一个Counter实例")
    }
  }
  getInstance() {
    return this
  }
  
  getCount() {
    return counter
  }
  
  increment() {
    return ++counter
  }

  decrement() {
    return --counter
  }
}

const counter1 = new Counter();
const counter2 = new Counter();
// Error: You can only create one instance!

这样就不能创建多个实例了

Object.freeze

这个时候我们需要导出我们的实例,但是在导出之前,我们应该使用Object.freeze方法确保初始化实例不会被修改,降低使用风险

代码语言:javascript
复制
const singletonCounter = Object.freeze(new Counter());
export default singletonCounter;

这样我们就将singletonCounter导出了,我们可以在任意JavaScript文件使用singletonCounter

在不同的文件调用,数据都是共享的,都能够改变counter值,并且能够读取到最新的值

优缺点

将实例化限制为一个实例会节省大量内存空间,不用每次都给新的实例分配内存,在整个应用中这个实例都能够被引用,但是单例模式被认为是一种反模式,应该在JavaScript中避免

其它的编程语言中,比如java或者c++,不可能跟javascript一样直接创建对象,在面向对象的编程语言中,需要创建一个类,这个类会创建一个对象,该创建的对象具有类实例的值,就像javascript中的实例值一样

其实上面的一系列的操作,完全可以使用一个简单的常规对象来替代,比如

代码语言:javascript
复制
let count = 0;

const counter = {
  increment() {
    return ++count;
  },
  decrement() {
    return --count;
  }
};

Object.freeze(counter);
export { counter };

这样可以实现Counter类等价的效果

还有一些隐藏风险,比如我们在一个单例中引入了另一个单例,这里我们创建了一个superCounter的实例,其中引入了Counter实例,在别的文件中如果引入Counter实例可能就会造成风险,一旦调用了super Counter,那Counter也会被改变

代码语言:javascript
复制
import Counter from "./counter";

export default class SuperCounter {
  constructor() {
    this.count = 0;
  }

  increment() {
    Counter.increment();
    return (this.count += 100);
  }

  decrement() {
    Counter.decrement();
    return (this.count -= 100);
  }
}

总结

一个单例实例应该能够在整个应用中被引用,拥有全局行为也会被觉得是一个糟糕的设计,因为你可以随意更改它,但是你并不知道你到底在哪里更改了它

在react中,经常通过redux或者react context等状态管理工具来进行全局状态管理,而不是使用单例模式,即使它们看起来这么像单例模式,这些工具提供了只读状态而不是单例的可变状态,使用redux时,只有纯函数的reducer可以在组建内部通过调度程序发送action进行状态更新,这些工具也存在全局状态的缺点,但是它可以让全局状态按照我们制定的规则或者顺序发生改变

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FE情报局 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前沿
  • 单例模式
  • 只能一个实例
  • Object.freeze
  • 优缺点
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档