实现单例模式
手写 JavaScript 单例模式的多种实现方式
问题
实现一个单例模式 (Singleton),确保一个类只有一个实例,并提供全局访问点。
解答
闭包实现
const Singleton = (function () {
let instance = null;
function createInstance(name) {
return {
name,
getName() {
return this.name;
},
};
}
return {
getInstance(name) {
// 如果实例不存在,创建一个
if (!instance) {
instance = createInstance(name);
}
// 返回已存在的实例
return instance;
},
};
})();
// 测试
const a = Singleton.getInstance('first');
const b = Singleton.getInstance('second');
console.log(a === b); // true
console.log(a.getName()); // 'first'
console.log(b.getName()); // 'first'
ES6 Class 实现
class Singleton {
static instance = null;
constructor(name) {
// 如果实例已存在,直接返回
if (Singleton.instance) {
return Singleton.instance;
}
this.name = name;
Singleton.instance = this;
}
getName() {
return this.name;
}
}
// 测试
const a = new Singleton('first');
const b = new Singleton('second');
console.log(a === b); // true
console.log(a.getName()); // 'first'
Proxy 实现
function createSingleton(ClassName) {
let instance = null;
return new Proxy(ClassName, {
construct(target, args) {
if (!instance) {
// 只在第一次调用时创建实例
instance = new target(...args);
}
return instance;
},
});
}
// 普通类
class User {
constructor(name) {
this.name = name;
}
}
// 转换为单例
const SingletonUser = createSingleton(User);
// 测试
const a = new SingletonUser('first');
const b = new SingletonUser('second');
console.log(a === b); // true
console.log(a.name); // 'first'
模块化单例(ES Module)
// singleton.js
class Database {
constructor() {
this.connection = null;
}
connect(url) {
if (!this.connection) {
this.connection = url;
console.log(`Connected to ${url}`);
}
return this.connection;
}
}
// ES Module 天然单例,导出的是同一个实例
export default new Database();
// 使用
// import db from './singleton.js'
// db.connect('mongodb://localhost')
关键点
- 单例模式确保一个类只有一个实例,多次调用返回同一对象
- 闭包实现通过私有变量
instance保存实例 - Class 实现利用静态属性存储实例,constructor 中判断并返回
- Proxy 实现可以将任意类转换为单例,不侵入原有代码
- ES Module 导出的对象天然是单例,模块只会执行一次
目录