title | date | tags | summary | ||
---|---|---|---|---|---|
设计模式——单例模式 |
2018-10-14 |
|
单例是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等。 |
单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点
单例是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等。
// 实现并不复杂,核心是利用一个变量来标志当前是否已经为某个类创建了单例
var Singleton = function(name){
this.name = name;
this.instance = null; // 标记和存放单例实例的容器
}
Singleton.prototype.getName = function(){
return this.name;
}
Singleton.getInstance = function(name){
if(!this.instance){ // 如果没有实例就创建并返回它
return this.instance = new Singleton(name);
}
// 如果已经有实例就返回它
return instance;
}
// 创建单例
var obj1 = Singleton.getInstance('william');
var obj2 = Singleton.getInstance('skye');
console.log(obj1===obj2); // true;
缺点即是不能通过new XXX方式获取实例,只能通过类提供的getInstance方法获取单例实例,容易被人在不知情的情况下直接new Singleton出没有单例的实例来。
可以实现通过new XXX方式获取实例,这样即可完成一个透明的单例
// 将构造函数封存在闭包
var Singleton = (function(){
var instance; // 标记和存放单例实例的容器
var Singleton = function(name){
if(instance){
return instance;
}
this.name = name;
// this.init(); // 一些初始化的操作,如有必要
return instance = this;
}
Singleton.prototype.init = function(args){
// todo;
}
return Singleton;
})();
// 创建单例
var obj1 = new Singleton('william');
var obj2 = new Singleton('skye');
console.log(obj1 === obj2); // true
为了把instance封装起来,我们使用了自执行的匿名函数和闭包,并且让这个函数将真正的Singleton构造方法返回出来,这增加了一些复杂度,阅读起来也不舒服。
这种在上述的代码中做了些调整,将构造单例的工作委托给了一个代理类,这样在开发中可以更加灵活的使用单例或者非单例;
// 依然创建一个普通的类
var Singleton = function(name){
this.name = name;
}
// 接下来引入代理类,将创建单例的工作委托给代理类
var ProxySingleton = (function(){
var instance; // 标记和容器
return function(name){
if(!instance){
return instance = new Singleton(name);
}
return instance;
}
})();
// 创建单例
var obj1 = new ProxySingleton('william');
var obj2 = new ProxySingleton('skye');
console.log(obj1 === obj2); // true
在需要的时候才创建对象,并且只创建唯一的一个。
在最开始的单例实现上就用了这种思想,当我们调用Singleton.getInstance
的时候才创建单例,而不是在页面加载好的时候就创建。
如果把通用的一些逻辑抽象出来就可以实现一个通用的惰性单例
单例的核心就是通过一个变量来标志是否创建了单例
// 核心逻辑
var obj;
if(!obj){
obj = xxx;
}
// 将这个逻辑抽离出来,封装在getSingleton函数内部,并将创建对象的方法fn当作参数动态地传入getSingleton
var getSingle = function(fn){
var instance;
return function(){
return instance || (instance = fn.apply(this.arguments));
}
}
// 使用
var createSingleLoginLayer = function(){
//创建一个登陆弹窗
var div = document.createElement('div');
div.innerHTML = '我是登陆浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
var createSigleLoginLayer = getSingle(createLoginLayer);
// 绑定按钮,点击创建唯一的浮窗
document.getElementById('loginBtn').onclick = function(){
var loginLayer = createSigleLoginLayer();
loginLayer.style.display = 'block';
}
// 创建一个登陆iframe
var createSingleIframe = getSingle(function(){
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
return iframe;
})
// 绑定按钮,点击创建唯一的iframe
document.getElementById('loginBtn').onclick = ()=>{
var loginLayer = createSingleIframe();
loginLayer.src = 'http://baidu.com';
}
首先学习了传统的单例模式的实现,也了解到因为语言的差异性,有更适合的方法在js中创建单例。特别是在合适的时机才创建唯一对象的惰性单例技术。更奇妙的是,创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。
关注本人公众号