此文僅記錄本人閱讀《JavaScript設計模式與開發實踐》這個本時的感受,感謝作者曾探寫出這麼好的一本書。如有冒犯,如有錯誤,請聯絡本人:luogao_lg@sina.com處理。
這一章讓我知道了單例模式的核心就是:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。但在JavaScript中單例模式有別的區別於傳統面嚮物件語言的應用,惰性單例模式在實際的開發中有很多用途,例如提高頁面效能,避免不必要的DOM操作等。
為何要有單例模式
書中有舉出一個實際場景,當我們點選登陸按鈕時,頁面中可能會出現一個彈框,而這個彈框是唯一的,無論點多少次登陸按鈕,彈框只會被建立一次,那麼這種情況下就適合用單例模式來建立彈框。
實現一個簡單的單例模式
以下程式碼來自書中
var CreateDiv = (function(html) {
var instance
var CreateDiv = function() {
if (instance) {
return instance
}
this.html = html
this.init()
return instance = this
}
CreateDiv.prototype.init = function() {
var div = document.createElement(`div`)
div.innerHTML = this.html
document.appendChild(div)
}
return CreateDiv
})()
複製程式碼
以上程式碼通過自執行函式和閉包將instance封裝起來。並且返回了真正的
Singleton
構造方法。
通過觀察上面程式碼發現CreateDiv
裡執行了兩個操作:
- 1.建立物件並且執行
init
方法。 - 2.保證只有一個物件。這裡就暴露出一個問題。
如果某天我們需要用這個方法向頁面中建立更多的元素。那我們必須要改寫CreateDiv
,如果我們結合“單一職責原則”,我們就知道要去把保證只有一個物件這個操作從CreateDiv
抽離出來。這個目的可以通過代理來實現。
用代理實現單例模式
首先我們把上面程式碼中的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.appendChild(div)
}
複製程式碼
接下來引入代理類
var ProxysingletonCreateDiv = (function() {
var instance
return function(html) {
if (!instance) {
instance = new CreateDiv(html)
}
return instance
}
})()
var a = new ProxysingletonCreateDiv(`test1`)
var b = new ProxysingletonCreateDiv(`test2`)
alert(a === b) // true
複製程式碼
至此利用代理類也實現了一個單例模式。但目前我們討論的單例模式跟接近傳統面嚮物件語言中的實現。接下來我們來了解一下JavaScript中的單例模式。
JavaScript中的單例模式——惰性單例
瞭解了單例模式的一些實現方法之後。我們可以來看看惰性單例的實現,這種實現方式在JavaScript的實際程式設計中是很實用的。
惰性單例
惰性單例是指在需要的時候才建立物件例項,而不是像之前的程式碼那樣,利用自執行函式在程式碼執行時就把物件例項建立。
比如最開始就提到,當開啟一個網站時,需要登入,但登陸的彈窗只會在點選登陸按鈕時出現,甚至有的網站不需要登入就能直接瀏覽。這時我們並不需要在頁面載入時就去建立一個彈窗。我們大可在需要用的時候去建立。
<html>
<body>
<button id="loginBtn">登入</button>
</body>
<script>
var createLoginLayer = (function() {
var div
return function() {
if (!div) {
var div = document.createElement(`div`)
div.innerHTML = `我是登入彈窗`
div.style.display = `none`
document.appendChild(div)
}
return div
}
})()
document.getElementById(`loginBtn`).onclick = function() {
var loginLayer = createLoginLayer()
loginLayer.style.display = `block`
}
</script>
</html>
複製程式碼
以上我們實現了一個單例模式的彈窗。但是我們還是可以把其中的控制只有一個物件的操作抽離出來,讓我們來實現一個通用的惰性單例。
通用惰性單例
通用惰性單例的實現就是要抽離所有單例模式都要實現的——控制只有一個物件。那麼我們來看看控制只有一個物件的操作抽象出來是個什麼樣子:
var obj
if (!obj) {
obj = xxx
}
複製程式碼
於是就可以把這個操作的邏輯封裝到一個getSingle
函式中,然後把要執行的函式當作引數傳入進去:
var getSingle = function(fn) {
var result
return function() {
result || (result = fn.apply(this, arguments))
}
}
複製程式碼
這樣我們上面寫的建立彈窗的方法就可以完全抽離出來:
var createLoginLayer = function() {
var div = document.createElement(`div`)
div.innerHTML = `我是登入彈窗`
div.style.display = `none`
document.appendChild(div)
return div
}
var createSingleLoginLayer = getsingle(createLoginLayer)
document.getElementById(`loginBtn`).onclick = function() {
var loginLayer = createSingleLoginLayer()
loginLayer.style.display = `block`
}
複製程式碼
至此我們實現了一個getSingle
函式來幫我們實現只有一個例項物件的目的,並且將例項物件要做的指責獨立出來,兩個方法互不打擾。
最後
原文出自 Roy`s Blog
參考
《JavaScript設計模式與開發實踐》—— 曾探