设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。对于js来说,设计模式就是一种将属性和方法组织在一起的一种方式
单例模式: 保证一个类仅有一个实例,并提供一个访问它的全局访问点
单例模式的应用:
- 线程池
- 全局缓存
- 浏览器中的window对象
单例模式的实现可以通过增加一个标志来实现: 给Singleton增加一个instance属性,初始时置为null,对Singleton实例化后,让instance指向实例化后的对象,通过返回instance,就可以保证并且获得唯一的实例。
添加函数属性实现单例模式
function Singleton(name){
this.name = name;
this.instance = null;
}
Singleton.getInstance = function (name){
if(!this.instance){
this.instance = new Singleton(name)
}
return this.instance;
}
Singleton.prototype.getName = function(){
return this.name;
}
// 测试
let a = Singleton.getInstance('John')
let b = Singleton.getInstance('Lili');
console.log(a);
console.log(b)
console.log(a === b)
但是这种方式实现的话,会给Singleton增加一个instance属性,如果不需要这个属性的话,可以将这个属性利用自执行的匿名函数和闭包,放在Singleton.getInstance的作用域中。
function Singleton(name){
this.name = name;
}
// 立即执行的函数,返回一个闭包函数
Singleton.getInstance = (function (){
let instance = null;
return function (name){
if(!instance){
instance = new Singleton(name)
}
return instance;
}
})();
Singleton.prototype.getName = function(){
return this.name;
}
// 测试
let a = Singleton.getInstance('John')
let b = Singleton.getInstance('Lili');
console.log(a);
console.log(b)
console.log(a === b)
以上两种实现方式相对简单,但是它与我们平时用new实例化方式不同,增加了这个类的“不透明性”。 那么接下来就通过使用new的方式来实现一下,其实也是利用立即执行的匿名函数和闭包将Singleton函数进行包装。
需要对是否采用new的形式进行判断,因为如果不是采用new的形式,那么this会指向window或者undefined,程序会报错。
new形式的单例模式实现
const Singleton = (function(){
// 标志
let instance = null;
// 返回的匿名函数
return function(name){
if(!instance){
// 判断是否是通过new的方式
if(new.target !== undefined){
this.name = name;
instance = this;
}
else{
// 不是new的方式则报错
throw new Error('must use new')
}
}
// 返回实例
return instance;
}
})();
// 测试
let a = new Singleton('John')
let b = new Singleton('Lili');
console.log(a);
console.log(b)
console.log(a === b)
上面这种实现方式,其实是将类的构造功能和单例功能耦合在一起,增加了程序的复杂度,也不利于理解。因此可以将这两个功能分开。引入代理类来实现单例的功能。
function Singleton(name){
this.name = name;
}
const ProxySingleton = (function(){
// 标志
let instance = null;
// 返回的匿名函数
return function(name){
if(!instance){
// 判断是否是通过new的方式
if(new.target !== undefined){
instance = new Singleton(name);
}
else{
// 不是new的方式则报错
throw new Error('must use new')
}
}
// 返回实例
return instance;
}
})();
// 测试
let a = new ProxySingleton('John')
let b = new ProxySingleton('Lili');
console.log(a);
console.log(b)
console.log(a === b)
可以对比一下,Singleton跟一开始一样,而ProxySingleton则与上面的实现方式基本一致,只是将this.name = name; instance = this; 替换成了instance = new Singleton(name);,但是职责划分的就很清晰了。而且ProxySingleton还可以通过传入函数的方式,作为一个通用的单例功能函数。