所谓单例模式,指某个类永远只会有一个实例。
单例模式所解决的问题是:当类实例可以共用的时候,防止频繁地创建和销毁类实例,节约资源。
单例模式的实现思路很简单:判断该类当前是否已经有实例,如果有则返回,如果没有则创建。
基于这个思路,提供两种实现方案。
1、基于类的方式实现
参考传统面向对象语言的实现方式,可以创建单例模式的类对象如下所示:
class Singleton {
constructor(name) {
this.name = name;
this.instance = null;
}
// 该接口用于对该类进行实例化
static getInstance(name) {
if(!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
}
功能测试验证如下:
let a = Singleton.getInstance('Ayaka');
let b = Singleton.getInstance('princess');
console.log(a === b); // true
2、基于闭包的方式实现
与传统面向对象语言不同,JavaScript 的闭包能力,使得在实现单例模式上能够更加便捷。
const createSingleInstance = (function () {
let instance = null;
return function(name) {
if (!instance) {
instance = {name};
}
return instance;
}
})();
// 测试
let a = createSingleInstance('Ayaka');
let b = createSingleInstance('princess');
console.log(a === b); // true
👆 这里我们通过一个匿名立即执行函数,创建了个闭包,将单例封装了起来。
这样做不够好的地方在于,如果我需要创建另一种单例对象,就得将这份代码拷贝下来,再修改里面的逻辑,如下所示。
const createOtherSingleInstance = (function () {
let instance = null;
return function(width, height) {
if (!instance) {
instance = {width, height};
}
return instance;
}
})();
优化一下,我们可以把管理单例的闭包逻辑提取成一个单独的函数进行处理,将创建对象和管理单例的职责被分布在两个不同的方法中。 👇
const getSingle = function(fn) {
let result;
return function(...args) {
return result || (result = fn.apply(this, args));
}
}
const createSingleInstance = getSingle(function(name) {
return {name};
})
const createOtherSingleInstance = getSingle(function(width, height) {
return {width, height}
})
// 测试
let a = createSingleInstance('Ayaka');
let b = createSingleInstance('princess');
console.log(a === b); // true
let c = createOtherSingleInstance(5, 10);
let d = createOtherSingleInstance(6, 11);
console.log(c === d); // true
最后再看看来自《JavaScript设计模式与开发实践》的相关实践代码 👇。
var getSingle = function(fn) {
var result;
return function() {
return result || (result = fn.apply(this, arguments));
}
}
// 使用一
var createLoginLayer = function() {
var div = document.createElement('div');
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
var createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};
// 使用二
const createSingleIframe = getSingle(function() {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
return iframe;
});
document.getElementById('loginBtn').onclick = function() {
var loginLayer = createSingleIframe();
loginLayer.src = 'http://baidu.com'
}
参考
《JavaScript设计模式与开发实践》 第4章 单例模式