JavaScript设计模式:单例模式

173 阅读4分钟

4.gif

模式概念

单例模式(Singleton Pattern)是一种对象创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。

它的核心思想是:

  1. 唯一性:保证一个类只有一个实例。
  2. 全局访问点:提供一种方式来获取这个唯一的实例。

通常用于管理共享资源,如配置信息、线程池、缓存等。

模式结构

Snipaste_2024-07-23_17-37-50.jpg

  • Singleton(单例类) :包含一个私有静态变量来持有类的唯一实例,并提供一个公有静态方法来获取这个实例。

代码实现

class Singleton {
  static instance = null;
​
  constructor() {
    // 私有构造函数
  }
​
  static getInstance() {
    if (!Singleton.instance) {
      // 确保只创建一个实例
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
​
  doSomething() {
    console.log("Doing something...");
  }
}
​
// 获取单例对象并调用方法
const singleton = Singleton.getInstance();
singleton.doSomething();
​
// 再次获取单例对象,将得到同一个实例
const anotherInstance = Singleton.getInstance();
console.log(singleton === anotherInstance); // 输出 true
  • 通过 Singleton.getInstance() 方法,无论何时调用,都将返回同一个实例。
  • 单例对象的创建是延迟的,即在第一次调用 getInstance() 方法时才创建。

也可以使用闭包来实现

const Singleton = (function () {
  let instance;
​
  function init() {
    // 私有变量和方法
    const privateMethod = function () {
      console.log("Private method");
    };
​
    return {
      publicMethod: function () {
        console.log("Public method");
        privateMethod();
      },
      // 可以在这里添加更多公开方法
    };
  }
​
  return {
    getInstance: function () {
      if (!instance) {
        instance = init();
      }
      return instance;
    },
  };
})();
// 获取单例对象并调用方法
const singleton = Singleton.getInstance();
singleton.publicMethod();
​
// // 再次获取单例对象,将得到同一个实例
const anotherInstance = Singleton.getInstance();
console.log(singleton === anotherInstance); // 输出 true

模式效果

模式的优点:

  • 资源节约:由于只有一个实例,因此可以节约系统资源。没有频繁的创建和销毁,占用内存的行为。
  • 一致性:确保系统中的数据一致性。
  • 控制全局访问:可以控制实例的访问方式。

模式的缺点:

  • 全局状态:单例类的职责过重,持有的全局状态可能导致代码难以测试和维护。
  • 扩展性:单例模式本身是解决扩展性问题的一种方式,一定程度上违背了单职责原则,过度使用可能导致代码难以扩展。
  • 多线程环境:在多线程环境中需要额外的处理来保证线程安全。(虽然在 JavaScript 的单线程环境中不常见)

在以下情况下可以使用单例模式:

  • 系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象。
  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该示例。

模式应用

在一些现有的 JavaScript 库中,单例模式也被广泛应用。例如:

jQuery

jQuery 库本身并不是一个典型的单例模式实现,但它的一些特性使得它在实际使用中表现为单例模式。这种模式使得 jQuery 在处理 DOM、事件和其他浏览器 API 时非常方便和强大。

特征:

  1. 全局访问点:jQuery 通过 $ 符号或jQuery方法提供了一个全局访问点,这使得你可以在任何地方访问 jQuery 实例。
  2. 唯一实例:尽管你可以多次调用 jQuery()$(),它们都会返回相同的 jQuery 对象实例。
// 初始化 jQuery 对象
const myjQueryObject = jQuery("<div>Hi, I'm a jQuery object!</div>");
​
// 另一种方式,使用 $ 作为别名
const myjQueryObject = $("<div>Hi, I'm a jQuery object!</div>");
​
// 访问全局 jQuery 对象
console.log(jQuery); // 输出 jQuery 对象
console.log($); // 输出 jQuery 对象

Pinia

Pinia 是 Vue 3 的官方状态管理库,它被设计为 Vuex 的替代品。Pinia 的核心是围绕单个 Store 实例构建的,这个 Store 实例在整个应用程序中是唯一的,这体现了单例模式的一些关键特征。

特征:

  1. 单个 Store 实例:在 Pinia 中,使用defineStore创建的每个 Store 都是一个单例。这意味着无论何时创建一个 Store 实例,都会返回相同的实例。
  2. 全局状态访问:Pinia 提供了一个全局的 useStore 钩子,允许你在应用程序的任何组件中访问 Store 实例。
  3. 集中状态管理:由于 Store 实例是单例的,Pinia 可以集中管理应用程序的状态。
//定义 Store
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})
​
​
//使用store
<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>

今天的分享就到这里,希望可以帮助到你!假如你对文章感兴趣,可以来我的公众号:小新学研社。

13.gif