单例模式在 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中的单例模式相比传统面向对象语言更灵活,可以根据具体需求选择适合的实现方式。