单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
在 JavaScript 开发中,有一些对象只需要一个。比如单击登录按钮,页面会出现登录对话框,并且是唯一的。
无论单击多少次按钮,登录对话框都只会被创建一次,那么这个登录对话框就比较适合用单例模式创建。
1. 单例模式的实现(不透明)
实现思路:通过一个变量标记当前是否已经为某个类创建实例,如果是则下一次获取该类的实例,否则进行新建。
const Singleton = function (name) {
this.name = name;
};
Singleton.prototype.getName = function () {
return this.name;
};
Singleton.getInstance = (function (name) {
let instance = null;
return function () {
if (!instance) {
instance = new Singleton(name);
}
return instance;
};
})();
const singleA = Singleton.getInstance("sven1");
const singleB = Singleton.getInstance("sven2");
console.log(singleA === singleB); // true
弊端:此方法通过 getInstance
可以保证只会创建一个实例,但是无法保证通过 new Singleton
这个方式创建其他的实例,所以意义不大。
2. 透明的单例模式
实现思路:通过必报和自执行匿名函数,可以把 instance
封装起来,不被外界访问得到。
const Singleton = (function () {
let instance = null;
function CreateSingleton(name) {
this.name = name;
}
CreateSingleton.prototype.getName = function () {
return this.name;
};
return function (name) {
if (!instance) {
instance = new CreateSingleton(name);
}
return instance;
};
})();
const singleA = new Singleton("sven1");
const singleB = new Singleton("sven2");
console.log(singleA === singleB); // true
弊端:通过匿名函数和闭包创建,增加了程序的复杂度,不易理解。
3. 代理实现单例模式
通过代理类,可以让 Singleton
变为一个普通的函数。
function Singleton(name) {
this.name = name;
}
Singleton.prototype.getName = function () {
return this.name;
};
const ProxySingleton = (function () {
let instance = null;
return function (name) {
if (!instance) {
instance = new Singleton(name);
}
return instance;
};
})();
const singleA = new ProxySingleton("sven1");
const singleB = new ProxySingleton("sven2");
console.log(singleA === singleB); // true
4. 惰性单例模式
惰性单例是指在需要的时候才创建对象实例。
// 将函数作为一个参数传递
const getSingle = function (fn) {
let result;
return function () {
// 通过apply的方式收集参数并执行传入的参数将结果返回
return result || (result = fn.apply(this, arguments));
};
};
这种方式最大的优点就是缓存了需要的结果,并且可以在需要的时候去调用,符合封装的单一职责。