前言
在软件开发中,有时我们希望某个类在系统中只存在一个实例,且能够被整个应用程序共享。例如,在一个大型系统中,如果每次访问某个资源都需要创建一个新的实例,不仅会浪费系统资源,还可能导致意想不到的问题。单例模式正是为了解决这个问题而设计的。
特点:
- 只有一个实例,全局都可以访问该实例
- 避免重复创建,减少内存占用。
饿汉单例
闭包实现
var SingleTon = (function () {
var instance;
function Instance() {}
return function () {
if (instance == undefined) {
instance = new Instance();
}
return instance;
};
})();
var s1 = SingleTon();
var s2 = SingleTon();
console.log(s1 === s2);
ES6
class SingleTon {
constructor() {
if (SingleTon.instance == undefined) {
SingleTon.instance = this;
}
return SingleTon.instance;
}
}
const s1 = new SingleTon();
const s2 = new SingleTon();
console.log(s1 === s2);
懒汉单例
懒汉单例(Lazy Singleton)模式是一种创建型设计模式,它延迟对象的创建直到第一次使用该对象时才进行。这种模式可以节省资源,尤其是在对象的创建成本较高或者对象不一定需要被创建的情况下。
经常在编写服务端代码时使用,web端几乎用不到。
var Singleton = (function () {
let instance;
function createInstance() {
const object = new Object("I am the instance");
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
},
};
})();
const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2); // true
适用场景:
- 需要唯一实例:确保在全局范围内只存在一个实例,并且所有访问该实例的请求都能获得相同的实例。
- 延迟加载:实例的创建比较耗时,或不一定在程序启动时就需要,可以通过惰性初始化来优化性能。
- 服务端:浏览器端一般通过实例化后赋值给公共对象(某一作用域中都能访问到的一个对象),或者将需要实例化的类挂载到全局,依赖于浏览器全局变量的特性;而服务端通常基于三层架构,在同一层的不同文件,一般会引入类文件然后调用获取类实例的方法来获取实例;
透明单例
透明单例(Transparent Singleton)是一种变体的单例模式,旨在让单例的使用更加简洁,不需要通过专门的方法来获取实例。使用透明单例时,类的实例化是自动进行的,开发者可以直接像使用普通类一样使用它,而不需要关心是否已经存在实例
class Singleton {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const SingletonProxy = new Proxy(Singleton, {
construct(target, args) {
if (!target.instance) {
target.instance = new target(...args);
}
return target.instance;
},
});
const s1 = new SingletonProxy("Instance 1");
const s2 = new SingletonProxy("Instance 2");
console.log(s1 === s2); // true
console.log(s1.getName()); // "Instance 1"
console.log(s2.getName()); // "Instance 2"
适用场景
- 开发者体验:当希望提供一个类,使用起来像普通类,但又保证了全局唯一性。
- 代码简洁:在大型项目中,通过透明单例可以减少不必要的复杂性,使代码更为直观。
我们还可以将 Proxy 的逻辑抽离出来,作为处理唯一性的公共方法
class Stu {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class Teacher {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const createProxy = function (originClass) {
return new Proxy(originClass, {
construct(target, args) {
if (!target.instance) {
target.instance = new target(...args);
}
return target.instance;
},
});
};
const StuProxy = createProxy(Stu);
const TeacherProxy = createProxy(Teacher);
const s1 = new StuProxy("zhangsan");
const s2 = new StuProxy("lisi");
const t1 = new TeacherProxy("lilei");
const t2 = new TeacherProxy("xiaoming");
console.log(s1 === s2); // true
console.log(t1 === t2); // true
总结
在JavaScript中,单例模式可能不是总是必要的,因为模块机制和全局对象已经提供了一种类似单例的功能。懒汉单例模式更适用于那些需要控制实例化时机的场景,大多数时候用于服务端的开发。