JavaScript 中的单例模式

发布于:2025-04-22 ⋅ 阅读:(17) ⋅ 点赞:(0)

单例模式在 JavaScript 中是一种确保类只有一个实例,并提供全局访问点的方式。由于 JavaScript 的语言特性(如对象字面量、模块系统等),实现单例有多种方式。

常见实现方式

1. 对象字面量(最简单的单例)

const singleton = {
  property: "value",
  method: function() {
    console.log("I am a method");
  }
};

// 使用
singleton.method();

特点

  • 对象字面量本身就是单例

  • 无法延迟初始化

  • 不能私有化成员

2. 闭包实现(带私有成员)

const Singleton = (function() {
  // 私有变量
  let instance;
  
  function init() {
    // 私有方法和属性
    const privateVar = "I am private";
    const privateRandom = Math.random();
    
    return {
      // 公共方法
      publicMethod: function() {
        console.log("Public can see me");
      },
      // 公共属性
      publicProperty: "I am public",
      // 访问私有变量的方法
      getPrivateVar: function() {
        return privateVar;
      },
      getRandomNumber: function() {
        return privateRandom;
      }
    };
  }
  
  return {
    getInstance: function() {
      if (!instance) {
        instance = init();
      }
      return instance;
    }
  };
})();

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

console.log(instance1 === instance2); // true
console.log(instance1.getRandomNumber() === instance2.getRandomNumber()); // true

特点

  • 真正的单例实现

  • 可以包含私有成员

  • 延迟初始化

  • 线程安全(JS是单线程)

3. ES6 Class 实现

class Singleton {
  constructor() {
    if (!Singleton.instance) {
      this._data = [];
      Singleton.instance = this;
    }
    return Singleton.instance;
  }
  
  add(item) {
    this._data.push(item);
  }
  
  get(id) {
    return this._data.find(item => item.id === id);
  }
}

// 使用
const instance1 = new Singleton();
const instance2 = new Singleton();

instance1.add({ id: 1, name: "test" });
console.log(instance2.get(1)); // { id: 1, name: "test" }
console.log(instance1 === instance2); // true

4. 模块模式(现代JS常用方式)

// singleton.js
let instance = null;
let data = []; // 私有变量

export default class Singleton {
  constructor() {
    if (!instance) {
      instance = this;
    }
    return instance;
  }
  
  add(item) {
    data.push(item);
  }
  
  get(id) {
    return data.find(item => item.id === id);
  }
  
  getAll() {
    return [...data];
  }
}

// 使用
import Singleton from './singleton.js';

const instance1 = new Singleton();
const instance2 = new Singleton();

instance1.add({ id: 1, name: "Item 1" });
console.log(instance2.getAll()); // [{ id: 1, name: "Item 1" }]

实际应用场景

  1. 全局状态管理

    // 类似Redux的store就是单例
    const store = {
      state: { count: 0 },
      increment() {
        this.state.count++;
      }
    };
  2. 对话框/模态框管理

    const Modal = (function() {
      let instance;
      
      function createModal() {
        const modal = document.createElement('div');
        // 初始化模态框...
        return {
          open: () => modal.style.display = 'block',
          close: () => modal.style.display = 'none'
        };
      }
      
      return {
        getInstance: function() {
          if (!instance) {
            instance = createModal();
          }
          return instance;
        }
      };
    })();
  3. 缓存系统

    const Cache = {
      data: {},
      set(key, value) {
        this.data[key] = value;
      },
      get(key) {
        return this.data[key];
      },
      clear() {
        this.data = {};
      }
    };

注意事项

  1. 模块系统本身就是单例:在现代JavaScript中,ES6模块默认就是单例

    // logger.js
    export default {
      log(message) {
        console.log(message);
      }
    }
    
    // 无论多少次import,得到的都是同一个对象
  2. 测试困难:单例可能导致测试困难,因为状态是共享的

  3. 全局污染:过度使用单例可能导致命名冲突和难以追踪的依赖关系

  4. Node.js中的单例:在Node.js中,模块缓存确保require多次返回同一个实例

JavaScript中的单例模式相比传统面向对象语言更灵活,可以根据具体需求选择适合的实现方式。


网站公告

今日签到

点亮在社区的每一天
去签到