前言
最近這段時間重構(gòu)了一些項目, 期間遇到的最大的問題就是怎樣保證代碼的健壯性。很多情況下由于寫的代碼考慮不完善,使得重構(gòu)的過程中有新的需求加入, 很多原先的邏輯不得不整體推翻重寫。在網(wǎng)上看了很多相關(guān)資料,受益匪淺??偟膩碚f寫代碼也是要有套路的。一個好的套路能夠讓我們的代碼有更好的可讀性,可維護(hù)性,也能提高自身編碼的層次。我看了一些設(shè)計模式有關(guān)的書,發(fā)現(xiàn)很多東西在寫的時候其實是有規(guī)律可循,比如一些常見的設(shè)計模式。在寫代碼的過程中根據(jù)項目的情況加入設(shè)計模式的思想,必將能夠很大程度上提升我們的代碼水平。 曾探《javascript 設(shè)計模式與開發(fā)實踐》這本書寫得非常好。雖然是前幾年出版的,但是里面很多思想一點也不過時。于是我就有了一個想法,以這本書為大綱, 我想通過我的理解將書中的精要簡略的寫出來..
單例模式
所謂單例模式 就是 一個類僅有一個實例并且提供一個它訪問全局的訪問方法
透明單例模式
現(xiàn)在我們有一個需求:在頁面中掉一個方法 始終只創(chuàng)建一個div 。這種場景一般可以應(yīng)用于創(chuàng)建遮罩層這種需求
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
單例模式 只有一個核心就是確保只有一個實例,并提供給全局訪問
上面的單例模式是從 “類” 中來展現(xiàn)的 ,在以類為中心的語言中 這是很自然的做法。JS是一門無類型的語言。我們要實現(xiàn)單例模式只要抓住一點就是 只有一個實例 并提供給全局訪問就可以了
全局變量不是單例模式 ,但是在JavaScript開發(fā)中我們經(jīng)常會把 全局變量當(dāng)成單例來使用
例如:
var a = {}
js 聲明全局變量會有很多問題比如 容易被其他模塊篡改,不利于模塊化在ES6 中推出了 let const 這種塊級作用域聲明方法。我們在創(chuàng)建對象以及其方法的過程中最好使用對象的字面量方式如:
let instance = {
aaa:function(){},
bbb:function(){}
}
惰性單例模式
前面的例子我們在初始化的時候就進(jìn)行了實例化, 所謂 惰性單例就是我們要在需要的時候才創(chuàng)建對象實例
例子: 登陸彈窗,需求是點擊登陸 彈出彈窗,
<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';
};
這個例子,我們點擊的時候才創(chuàng)建 dom實例, 通過閉包, 下一次點擊的時候還是返回之前的實例
通用的惰性單例
上面的代碼很好的實現(xiàn)了一個 我們的需求,但是還是存在一些問題
- 代碼違反了單一職責(zé)的原則 ,創(chuàng)建對象和管理單例的邏輯都煩剛在createLoginLayer對象內(nèi)部
- 如果下次我們需要創(chuàng)建頁面中唯一的iframe,或者 script 標(biāo)簽,用來跨域請求數(shù)據(jù),就 必須得如法炮制,把 createLoginLayer 函數(shù)幾乎照抄一遍
var createIframe= (function(){
var iframe;
return function(){
if ( !iframe){
iframe= document.createElement( 'iframe' );
iframe.style.display = 'none';
document.body.appendChild( iframe);
}
return iframe;
}
})();
我們現(xiàn)在需要把不變的分離出來,管理單例的邏輯其實是可以抽象出來的,這個邏輯始終是: 用一個變量來標(biāo)志是否創(chuàng)建過對象,如果是 ,則返回這個已經(jīng)創(chuàng)建好的對象
var obj;
if( !obj){
obj = xxx;
}
我們來看優(yōu)化后的代碼
//管理單例的邏輯 抽象出來
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';
};
// 創(chuàng)建一個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';
};
在這個例子中,我們把創(chuàng)建實例對象的職責(zé)和管理單例的職責(zé)分別放置在兩個方法里,這兩 個方法可以獨(dú)立變化而互不影響,當(dāng)它們連接在一起的時候,就完成了創(chuàng)建唯一實例對象的功能, 看起來是一件挺奇妙的事情
本文部分摘錄自 曾探《javascript 設(shè)計模式與開發(fā)實踐》這本書寫得相當(dāng)出彩 強(qiáng)烈推薦