前言
最近这段时间重构了一些项目, 期间遇到的最大的问题就是怎样保证代码的健壮性。很多情况下由于写的代码考虑不完善,使得重构的过程中有新的需求加入, 很多原先的逻辑不得不整体推翻重写。在网上看了很多相关资料,受益匪浅。总的来说写代码也是要有套路的。一个好的套路能够让我们的代码有更好的可读性,可维护性,也能提高自身编码的层次。我看了一些设计模式有关的书,发现很多东西在写的时候其实是有规律可循,比如一些常见的设计模式。在写代码的过程中根据项目的情况加入设计模式的思想,必将能够很大程度上提升我们的代码水平。 曾探《javascript 设计模式与开发实践》这本书写得非常好。虽然是前几年出版的,但是里面很多思想一点也不过时。于是我就有了一个想法,以这本书为大纲, 我想通过我的理解将书中的精要简略的写出来..
单例模式
所谓单例模式 就是 一个类仅有一个实例并且提供一个它访问全局的访问方法
透明单例模式
现在我们有一个需求:在页面中掉一个方法 始终只创建一个div 。这种场景一般可以应用于创建遮罩层这种需求
var createDom = (function(){
var instance;
var createDom = function( html ){
if ( instance ){
return instance;
}
this.html = html; this.init();
return instance = this;
};
createDom.prototype.init = function(){
var div = document.createElement( 'div' );
div.innerHTML = this.html;
document.body.appendChild( div );
};
return createDom;
})();
var a = new createDom( 'div1' );
var b = new createDom( 'div2' );
alert ( a === b ); // true
单例模式 只有一个核心就是确保只有一个实例,并提供给全局访问
上面的单例模式是从 “类” 中来展现的 ,在以类为中心的语言中 这是很自然的做法。JS是一门无类型的语言。我们要实现单例模式只要抓住一点就是 只有一个实例 并提供给全局访问就可以了
全局变量不是单例模式 ,但是在JavaScript开发中我们经常会把 全局变量当成单例来使用
例如:
var a = {}
js 声明全局变量会有很多问题比如 容易被其他模块篡改,不利于模块化在ES6 中推出了 let const 这种块级作用域声明方法。我们在创建对象以及其方法的过程中最好使用对象的字面量方式如:
let instance = {
aaa:function(){},
bbb:function(){}
}
惰性单例模式
前面的例子我们在初始化的时候就进行了实例化, 所谓 惰性单例就是我们要在需要的时候才创建对象实例
例子: 登陆弹窗,需求是点击登陆 弹出弹窗,
<button id="loginBtn">登录</button>
var createLoginLayer = (function(){
var div;
return function(){
if ( !div ){
div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
}
return div;
}
})();
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createLoginLayer();
loginLayer.style.display = 'block';
};
这个例子,我们点击的时候才创建 dom实例, 通过闭包, 下一次点击的时候还是返回之前的实例
通用的惰性单例
上面的代码很好的实现了一个 我们的需求,但是还是存在一些问题
- 代码违反了单一职责的原则 ,创建对象和管理单例的逻辑都烦刚在createLoginLayer对象内部
- 如果下次我们需要创建页面中唯一的iframe,或者 script 标签,用来跨域请求数据,就 必须得如法炮制,把 createLoginLayer 函数几乎照抄一遍
var createIframe= (function(){
var iframe;
return function(){
if ( !iframe){
iframe= document.createElement( 'iframe' );
iframe.style.display = 'none';
document.body.appendChild( iframe);
}
return iframe;
}
})();
我们现在需要把不变的分离出来,管理单例的逻辑其实是可以抽象出来的,这个逻辑始终是: 用一个变量来标志是否创建过对象,如果是 ,则返回这个已经创建好的对象
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 = 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';
};
// 创建一个iframe 的代码
var createSingleIframe = getSingle( function(){
var iframe = document.createElement ( 'iframe' );
document.body.appendChild( iframe );
return iframe;
});
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleIframe();
loginLayer.src = 'http://baidu.com';
};
在这个例子中,我们把创建实例对象的职责和管理单例的职责分别放置在两个方法里,这两 个方法可以独立变化而互不影响,当它们连接在一起的时候,就完成了创建唯一实例对象的功能, 看起来是一件挺奇妙的事情
本文部分摘录自 曾探《javascript 设计模式与开发实践》这本书写得相当出彩 强烈推荐