找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1249

积分

0

好友

217

主题
发表于 5 天前 | 查看: 19| 回复: 0

单例模式是设计模式中的一种,其核心在于确保一个类在整个应用生命周期内只有一个实例,并提供唯一的全局访问点。这种模式在前端应用中的状态管理、缓存管理或数据库连接池等场景非常有用。以下将详细介绍在 JavaScript 中实现单例模式的六种常见方式。

一、对象字面量

这是创建单例最简单直接的方式,通过对象字面量快速定义一个对象。

const Singleton = {
  property: 'value',
  method() {
    // 注意:方法内使用 this 在被解构调用时可能丢失上下文
    // console.log(this.property);
    // 更安全的做法是直接引用单例对象本身
    console.log(Singleton.property);
  }
};

// 使用
Singleton.method();

此方法无需实例化,定义即单例。唯一需要注意的是,如果将其方法解构出来单独调用,this指向会出问题,因此建议在方法内部直接通过对象名(Singleton)访问属性。

二、闭包与立即执行函数

利用 JavaScript 闭包的私有性,结合立即执行函数(IIFE)来封装实例变量。

const Singleton = (function() {
  // 私有变量,用于存储唯一实例
  let instance;

  // 私有构造函数
  function createInstance() {
    const object = new Object('I am the instance');
    return object;
  }

  // 对外暴露的公共 API
  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

// 使用
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

这种方式将创建实例的逻辑和实例本身完全封装在闭包内,外部只能通过getInstance方法获取唯一实例,实现了良好的封装。

三、ES6 Class 基础版

利用类的静态属性来存储唯一实例,并在构造函数中进行控制。

class Singleton {
  constructor() {
    // 如果已存在实例,则直接返回该实例
    if (Singleton.instance) {
      return Singleton.instance;
    }
    this.data = 'Singleton Data';
    // 将当前创建的实例赋值给静态属性
    Singleton.instance = this;
  }

  getData() {
    return this.data;
  }

  setData(data) {
    this.data = data;
  }
}

// 使用
const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1 === s2); // true
s1.setData('New Data');
console.log(s2.getData()); // ‘New Data’

这种实现直观,但构造函数并非完全私有,仍可通过new关键字调用,存在被多次实例化的风险(尽管后续调用会返回第一个实例)。

四、改进的 Class 实现 (TypeScript)

在 TypeScript 中,我们可以利用 private 构造器和静态方法,写出更健壮、更符合面向对象特性的单例。

class Singleton {
  // 静态私有属性,存储唯一实例
  private static instance: Singleton;

  // 私有构造函数,防止外部使用 new 创建
  private constructor() {}

  // 公开的静态方法,用于获取唯一实例
  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }

  // 其他实例方法...
}

// 使用
const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2); // true

这是目前较为推荐的实现方式之一。私有构造函数彻底杜绝了从外部 new 的可能,获取实例的唯一途径就是调用静态方法 getInstance,逻辑清晰且类型安全。

五、ES6 模块模式的单例

利用 ES6 模块作用域内的变量是单例的特性,可以非常优雅地实现单例模式。这在管理数据库或缓存连接等场景中尤为常见。

// database.js 模块文件
let instance = null;

class Database {
  constructor(config) {
    if (instance) {
      return instance;
    }
    this.connection = this.connect(config);
    instance = this;
  }

  connect(config) {
    // 模拟连接逻辑
    return { connected: true, config };
  }
}

// 导出一个工厂函数,用于获取唯一实例
export const getDatabaseInstance = (() => {
  let instance = null;
  return (config) => {
    if (!instance) {
      instance = new Database(config);
    }
    return instance;
  };
})();

在这个例子中,模块内部的 instance 变量对于导入该模块的所有地方都是共享的,从而保证了 Database 类的唯一性。导出一个封装好的工厂函数,使用起来更方便。

六、ES6 模块即单例

这是最简单也最被低估的一种方式。在 ES6 模块系统中,一个模块如果导出一个已实例化的对象,那么这个对象在所有引入它的地方都是同一个实例。

// database.js
class Database {
  constructor(config) {
    this.connection = this.connect(config);
  }
  connect(config) {
    return { connected: true, config };
  }
}

// 直接导出唯一的实例
export default new Database({ host: 'localhost' });

// app.js
import dbInstance from ‘./database.js’;
// 无论被多少个其他模块引入,dbInstance 都是同一个对象

这种方式无需任何额外的单例控制代码,完全依赖模块系统本身的特性,非常适合在现代前端工程化应用中使用。

总结与选择建议

以上六种方法可归纳为四大类:对象字面量、闭包、ES6 Class 和 ES6 模块模式。

  • 对象字面量:适合简单配置对象或工具集。
  • 闭包:兼容性好,封装性强,适合传统脚本或库的开发。
  • ES6 Class (TypeScript):代码结构清晰,类型安全,是当前推荐的主流方式。
  • ES6 模块:最为简洁自然,是现代模块化开发中的首选方案,尤其在与 VueReact 等框架结合时。

选择时,应根据项目环境(是否使用TypeScript、模块化规范)和具体场景(需要延迟初始化、线程安全等)来做出最适合的决策。




上一篇:iOS代码泄露揭示苹果产品路线图:折叠屏iPhone与M6芯片细节曝光
下一篇:特斯拉初代Roadster 2008-2010款设计文件开源:原理图与BOM详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 19:22 , Processed in 0.276652 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表