作為一個半路出家的前端恶导,隨著項目經(jīng)驗的積累,也越來越意識到原生js的博大精深腊徙,最近正在研究js設(shè)計模式屋摔,接下來每學(xué)一個設(shè)計模式就是寫篇文章做筆記烁设,其實主要還是代碼和設(shè)計思想的結(jié)合,努力體會,多思考合適自己項目中的應(yīng)用場景装黑,爭取實際應(yīng)用到實際項目中副瀑。
話不多說,進入主題恋谭,js設(shè)計模式之:單例模式
單例模式定義: 保證一個類僅有一個實例糠睡,并提供一個訪問它的全局訪問點
上代碼:
標準單例模式
//方法1
//標準單例模式
var Singleton = function(name){
this.name = name
}
Singleton.prototype.getName = function(){
console.log(this.name);
}
Singleton.getInstance = (function(){ //注意這里的getInstance方法是靜態(tài)方法
var instance = null
//這里使用閉包,作用在于只有當調(diào)用到getInstance方法時才進行單例控制疚颊,這是一種惰性單例模式狈孔,相比標準單例中將instance變量放在Singleton構(gòu)造函數(shù)中性能更好
return function(){
if(!instance){
instance = new Singleton(name)
}
return instance
}
})()
var a = Singleton.getInstance('nitx')
var b = Singleton.getInstance('sxm')
console.log(a === b); //true
上述通過Singleton.getInstance來獲取Singleton類的唯一對象,這種方法相對簡單材义,但有個問題均抽,增加了類的不透明性,Singleton類的使用者必須知道這是一個單例類其掂,跟以往通過new XXX的方式來獲取對象實例不同油挥,這里需要使用Singleton.getInstance來獲取對象。這種寫法意義不大款熬。
//方法二
//透明的單例模式喘漏,用戶從這個類中創(chuàng)建對象時,可以像使用其他任何普通類一樣华烟,通過new創(chuàng)建類實例。下面單例類的作用是在頁面中創(chuàng)建唯一的div節(jié)點持灰。
var CreateDivWrap = (function(){
var instance = null;
var CreateDiv = function(html){
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.body.appendChild(div)
}
return CreateDiv
})()
var c = new CreateDivWrap('nitx')
var d = new CreateDivWrap('sxm')
console.log(c === d); //true
上述代碼中盔夜,還有一個缺點,為了把instance封裝起來堤魁,使用了自執(zhí)行匿名函數(shù)和閉包喂链,并且讓這個匿名函數(shù)返回真正的Singleton構(gòu)造方法CreateDiv,這增加程序復(fù)雜度妥泉,CreateDiv方法負責(zé)兩件事椭微,一件是創(chuàng)建對象和執(zhí)行初始化init方法,第二是保證只有一個對象盲链,這種操作違背“單一職責(zé)原則”蝇率,假如將來要利用這個類在頁面中創(chuàng)建多個類,也就是讓這個類從單例類變成普通類刽沾,那就得改寫CreateDivWrap構(gòu)造函數(shù)本慕,去掉控制唯一對象的那一段代碼,麻煩侧漓!解決這個問題的方法是使用 代理實現(xiàn)單例模式
//方法三:
//先創(chuàng)建普通類锅尘,作用是創(chuàng)建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)
}
//創(chuàng)建并引用代理類ProxySingletonCreateDiv,其作用是控制CreateDiv類創(chuàng)建唯一對象
var ProxySingletonCreateDiv = (function(){
var instance = null
return function(html){
if(!instance){
instance = new CreateDiv(html)
}
return instance
}
})()
var e = new ProxySingletonCreateDiv('nitx')
var f = new ProxySingletonCreateDiv('sxm')
console.log(e === f);
通過引入代理類的方式布蔗,同樣完成一個單例模式的編寫藤违,但和之前方法一浪腐、二相比,把負責(zé)管理單例的邏輯移到了代理類ProxySingletonCreateDiv中顿乒,這樣CreateDiv就是一個普通類议街,CreateDiv類和ProxySingletonCreateDiv類組合起來就是一個單例類。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button type="button" id="btn1">登錄</button>
</body>
<script>
//添加dom普通寫法淆游,非單例傍睹,可無限添加dom,如需保持只有一個dom時犹菱,在點擊事件中加入刪除先前dom的操作
var createLoginLayer = function(){
var div = document.createElement('div')
div.innerHTML = '我是登錄彈窗'
div.style.display = 'none'
document.body.appendChild(div)
return div
}
document.getElementById('btn1').onclick = function(){
var loginLayer = createLoginLayer()
loginLayer.style.display = 'block'
}
//添加dom單例寫法拾稳,只能添加一個dom節(jié)點,這種寫法缺點是把單例邏輯耦合在具體業(yè)務(wù)代碼中腊脱,難以利用
var createLoginLayer = (function(){
var singleResult = null;
return function(){
if(!singleResult){
div = document.createElement('div')
div.innerHTML = '我是登錄彈窗'
div.style.display = 'none'
document.body.appendChild(div)
}
return singleResult = div
}
})()
document.getElementById('btn1').onclick = function(){
var loginLayer = createLoginLayer()
loginLayer.style.display = 'block'
}
//單獨抽出的單例邏輯
var getSingle = function(fn){
var result = null; //創(chuàng)建一個變量來標記是否創(chuàng)建過對象访得,如果是則下次直接返回已創(chuàng)建過的對象
return function(){
if(!result){
result = fn.apply(this, arguments) //執(zhí)行createLoginLayer函數(shù)結(jié)果返回div
}
return result
}
}
var createLoginLayer = function(){
div = document.createElement('div')
div.innerHTML = '我是登錄彈窗'
div.style.display = 'none'
document.body.appendChild(div)
return div
}
var createSingleLoginLayer = getSingle(createLoginLayer)(1, 2, 3)
document.getElementById('btn1').onclick = function(){
var loginDialog = createSingleLoginLayer()
loginDialog.style.display = 'block'
}
//第三個例子中,把創(chuàng)建實例對象的職責(zé)和管理單例的職責(zé)分別放置在兩個方法中陕凹,這兩個方法可以獨立變化而互不影響悍抑,當組合在一起時,就完成了創(chuàng)建唯一實例對象的功能
</script>
</html>
單例模式使用要點杜耙,應(yīng)結(jié)合閉包搜骡、高階函數(shù)、惰性單例佑女、創(chuàng)建實例對象與管理單例職責(zé)的方法拆分记靡。