单例模式在 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" }]
实际应用场景
-
全局状态管理:
// 类似Redux的store就是单例 const store = {state: { count: 0 },increment() {this.state.count++;} };
-
对话框/模态框管理:
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;}}; })();
-
缓存系统:
const Cache = {data: {},set(key, value) {this.data[key] = value;},get(key) {return this.data[key];},clear() {this.data = {};} };
注意事项
-
模块系统本身就是单例:在现代JavaScript中,ES6模块默认就是单例
// logger.js export default {log(message) {console.log(message);} }// 无论多少次import,得到的都是同一个对象
-
测试困难:单例可能导致测试困难,因为状态是共享的
-
全局污染:过度使用单例可能导致命名冲突和难以追踪的依赖关系
-
Node.js中的单例:在Node.js中,模块缓存确保require多次返回同一个实例
JavaScript中的单例模式相比传统面向对象语言更灵活,可以根据具体需求选择适合的实现方式。