设计模式(Design
pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
本文系列主要针对JavaScript语言特性全面总结了16个常用的设计模式,讲解了JavaScript面向对象和函数式编程方面的基础知识,介绍了面向对象的设计原则及其在设计模式的体现,本文将教会你如何把经典的设计模式应用到JavaScript语言中,编写出优美高效,结构化和可维护的代码。
单例模式 :
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池,全局缓存,浏览器中的window对象等。在JavaScript开发中,单例模式的用途同样非常广泛。试想一下,当我们点击登录按钮的时候,在页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。
实现单例模式
要实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。
参见代码:
var Singleton = function (name) {
this.name = name;
this.instance = null;
}
Singleton.prototype.getName = function () {
alert(this.name);
}
Singleton.getInstance = function (name) {
if(!this.instance){ // 如果实例不存在则创建一个实例
this.instance = new Singleton(name)
}
return this.instance;
};
var a = Singleton.getInstance('ritsu1');
var b = Singleton.getInstance('ritsu2');
alert(a === b) //true
判断为true也就说明了之后所创建的任何实例其实都是第一个,可自行控制台测试:
ok,现在虽然已经完成了一个单例模式的编写,但这段单例模式代码的意义并不大。
代理实现单例模式
我们现在的目标是引入代理类实现一个透明的单例类,用户从这个类中创建对象的时候,可以像使用任何普通类一样。 在下面的例子中,我们将使用CreateDiv单例类,它的作用是负责创建唯一的div节点,代码如下:
var CreateDiv = function(html) {
this.html = html;
this.init();
};
CreateDiv.prototype.init = function () {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div);
};
var ProxySingletonCreateDiv = (function () {
var instance;
return function (html) { // 这里使用了闭包的一些特性
if(!instance){
instance = new CreateDiv(html);
}
return instance;
}
})();
var a = new ProxySingletonCreateDiv('ritsu1');
var b = new ProxySingletonCreateDiv('ritsu2');
alert(a === b) //true
判断为true也就说明了之后所创建的任何实例其实都是第一个,可自行控制台测试:
前面提到的二种单例模式的实现,更多的是接近传统面向对象中的实现,单例对象由类而来,下面我们讲讲JavaScript的单例模式
单例模式的核心是确保只有一个实例,并提供全局访问。
惰性单例
惰性单例是指在需要的时候才创建对象实例。惰性单例是单例模式的重点,这种技术在实际开发中非常有用。
实例:
假设我们是WebQQ的开发人员,当点击左边导航里qq头像时,会弹出一个登陆浮窗,很明显这个浮窗是在页面里总是唯一的,不可能出现两个登陆窗口的情况。
实现原理: 用一个变量判断是否已经创建过登陆浮窗; 代码如下:
var createLoginLayer = (function () {
var div ;
return function () { //惰性单例的主要实现原理是利用闭包的特性!!!
if(!div){
div = document.createElement('div')
div.innerHTML = 'I am login window'
div.style.display = 'none'
document.bodyk.appendChild(div);
}
return div;
}
})();
document.getElementById('loginBtn').onclick = function () {
var loginLayer = createLoginLayer();
loginLayer.style.display = 'block';
}
通用的惰性单例:
上一个例子我们完成了一个可用的惰性单例,但是我们发现它还有如下一些问题。
如果我们要创建其他唯一的标签如script标签,用来跨域请求必须得如法炮制,把createLoginLayer函数几乎照抄一遍:
var createLoginLayer = (function () {
var div ;
return function () { //惰性单例的主要实现原理是利用闭包的特性!!!
if(!div){
script = document.createElement('script')
script.innerHTML = 'I am login window'
script.style.display = 'none'
document.bodyk.appendChild(script);
}
return script;
}
})();
这显然是痛苦且不符合可复用的设计模式的。 那么我们现在开始实现可复用通用的惰性单例。
我们需要把不变的部分隔离出来,先不考虑创建一个div和创建一个script有多少差异,先抽象出来一个逻辑:
用一个变量来标志是否创建过对象,如果是,则在下次直接返回这个创建好的对象:
var obj;
if(!obj)
obj = xxx
开始行动利用高阶函数把函数当做对象传入:
var getSingle = function (fn) {
var result;
return function () {
return result || (result = fn.apply(this,arguments));
}
};
还有之前的标签创建函数:
var createLoginLayer = (function () {
var div ;
return function () {
if(!div){
div = document.createElement('div')
div.innerHTML = 'I am login window'
div.style.display = 'none'
document.bodyk.appendChild(div);
}
return div;
}
})();
最后的创建:
var createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById('loginBtn').onclick = function () {
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
}
随便实现一个iframe标签:
var createSingleframe = getSingle( //这里我们直接放入一个匿名函数惹
function () {
var iframe = document.createElement('iframe')
document.body.appendChild(iframe)
return iframe
}
)
document.getElementById('loginBtn').onclick = function () {
var loginLayer = createSingleframe();
loginLayer.style.display = 'block';
}
至此我们大功告成了!!!
补充知识:
jquery的one事件:
当点击 p 元素时,增加该元素的文本大小:
$("p").one("click",function(){
$(this).animate({fontSize:"+=6px"}); });
one 事件只能触发一次,我们可以用惰性单例模式同样实现:
var bindEvent = getSingle(function () {
document.getElementById('div1').onclick = function () {
alert('click')
}
return true;
})
var render = function () {
console.log('start render ...')
bindEvent()
}
render()
render()
render()
小结
--
单例模式是我们学习的第一个模式,我们先学习了传统的单例模式实现,也了解到因为语言的差异性,由更适合JavaScript的实现方法。
在getSingle 函数中,提到了闭包和高阶函数的概念。单例模式是一种简单但非常实用的模式,特别是惰性单例技术,在适合的时候才创建对象,并且只创建唯一的一个。
注:本文是根据JavaScript设计模式与开发实践这本书而总结实现的
作者:颜卿今天Coding了吗
链接:http://www.jianshu.com/p/9c8c8c39ad61
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。