js设计模式-单例模式

单例模式是一种常见的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等。在js开发中,单例模式的用途同样非常广泛......
单例模式的定义
- 定义:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现单例模式
- 原理:
用一个变量来保存当前是否已经为某个类创建对象,如果创建了就直接返回该对象,反之就重新实例化
var Fn = function(name){
this.name = name;
}
Fn.prototype.getName = function(){
console.log(this.name);
}
Fn.getInstance = (function(){
var instance = null;
return function(name){
if(!instance){
instance = new Fn(name);
}
return instance;
}
})();
var a = Fn.getInstance('xxx');
var b = Fn.getInstance('ooo');
console.log(a === b);//true
透明的单例模式
我们创建一个CreateDiv单例类来负责在页面中创建唯一的div节点,实现一个**‘透明’**的单例类,用户从这个类中创建对象的时候,可以像使用任何普通类一样。
var CreateDiv = (function(){
var instance;
var CreateDiv = function(html){
if(instance){
return instance;
}
this.html= html;
this.init();
return instance = this;
};
CreateDiv.prototype.init = function(){
var div = $('<div></div>');
div.html(this.html);
$(body).append(div);
}
return CreateDiv;
})()
var a = new CreateDiv('one');
var b = new CreateDiv('two');
console.log(a === b); //true
上面的代码实现了**透明**单例,但是也有它的缺点,上面我们使用了自执行函数和闭包,并且让这个匿名函数返回真正的构造函数,这样增加了一些程序的复杂度。
如果某天我们需要利用这个类,在页面上创建很多个div,既要让这个单例类变成一个普通的可以产生多个实例,那我们就需要修改CreateDiv构造函数,把控制创建唯一对象的那段代码删除掉,这样就会刚给我带来一些不必要的麻烦,所有我们利用另外一种方式去实现,这种方式叫着**代理模式**,后面的笔记中我会详细介绍**代理模式**的具体实习方式
利用代理模式实现单例模式
我们把上面的透明单例代码中间创建div部分代码移除出去,使他成为一个普通的创建DIV类
//创建div类
var CreateDiv = function(html){
this.html = html;
this.init();
}
CreateDiv.prototype = function(){
var div = $('<div></div>');
div.html(this.html);
$('body').append(div);
}
//代理类
var ProxySingleton = (function(){
var instance;
return function(){
if(!instance){
instance = new CreateDiv(html);
}
return instance;
}
})()
//测试
var a = new ProxySingleton('one');
var b = new ProxySingleton('two');
console.log(a === b );//true
通过引入代理类的方式,我们也实现了单例模式,我们把负责管理单例的逻辑移到代理类**ProxySingleton**中,**CreateDiv**就变成一个普通类,它更**ProxySingleton**组合起来就可以实现单例模式的效果,这样的代码会更叫好维护。
JavaScript中的单例模式
- 上面提到的创建单例模式,更多的是接近面向对象语言中的实现,单例对象从**‘类’**创建而来,在类为中心得语言中,这是很自然的做法。
- 在JavaScript中,其实是一门无类语言,没有类这个概念,在JS中创建单例模式方法很简单,我们只需要创建一个唯一的类。
- 全局变量不是单例模式,但是在js开发中,我们经常把全局变量当成单例模式来使用
我们在js开发中有必要减少全局变量的使用,如果使用了也要把它的污染降到最低,以下两种方式就是降低全局变量带来的命名污染
-
使用命名空间
使用对象字面量的方式来创建:
var name = { a:function(){ console.log('1'); }, b:function(){ console.log('2'); } };使用动态创建命名空间
var My = {}; My.name = function(name){ var parts = name.split('.'); var current = My; for(var i in parts){ if(!current[parts[i]]){ current[parts[i]] = {}; } current = current[parts[i]]; } } My.name = ('init'); My.name = ('dom.style'); //上面创建结果等价于: var My = { init:{}, dom:{ style:{} } }; -
使用闭包封装私有变量
var user = (function(){
var __name = 'anjie',
__age = 25,
return {
getUserInfo:function(){
return __name + '-' + __age;
}
}
})();
我们使用下划线来约定私有变量__name和__age,他们被封装在闭包产生的作用域中,外部是访问不到这两个变量的,这样就避免了对全局的命令污染。
通用单例模式
var getSingle = function(fn){
var result;
return function(){
return result || (result = fn.apply(this,arguments));
}
}
//实例
var createDiv = function(){
var div = $('<div></div>');
div.html('这个是测试div');
div.css('display','none');
$('body').append(div);
return div;
}
var createSingleLoginLayer = getSingle(createDiv);
$('loginBtn').on('click',function(){
var loginLayer = createSingleLoginLayer();
loginLayer.css('display','block');
})
上面代码是一个通用的单例模式,我们在日常开发中可以直接利用这段代码来实现单例模式。
以上代码借鉴于《JavaScript设计模式与开发实践》 -- 曾探,这本书对设计模式进行的详细的介绍,推荐大家学习