JavaScript設(shè)計(jì)模式之單例模式應(yīng)用場(chǎng)景案例詳解
單例模式
如果希望自己的代碼更優(yōu)雅、可維護(hù)性更高以及更簡(jiǎn)潔,往往離不開(kāi)設(shè)計(jì)模式這一解決方案。
在JS設(shè)計(jì)模式中,最核心的思想:封裝變化(將變與不變分離,確保變化的部分靈活,不變的部分穩(wěn)定)。
那么來(lái)說(shuō)說(shuō)第一個(gè)常見(jiàn)的設(shè)計(jì)模式:單例模式。
單例模式保證一個(gè)類(lèi)僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)方式,為了解決一個(gè)全局使用的類(lèi)頻繁被創(chuàng)建和銷(xiāo)毀、占用內(nèi)存的問(wèn)題。
ES5中通過(guò)閉包
在ES5中,可以使用閉包(函數(shù)內(nèi)部返回函數(shù)被外界變量所引用,導(dǎo)致這個(gè)函數(shù)里面的變量無(wú)法被釋放,就構(gòu)建成閉包)來(lái)保存這個(gè)類(lèi)的實(shí)例。
var Singeton = (function(){
var instance;
function User(name,age){
this.name=name;
this.age=age;
}
return function(name,age){
if(!instance){
instance = new User(name,age)
}
return instance
}
})()此時(shí)這個(gè)實(shí)例一旦生成,每次都是返回這個(gè)實(shí)例,且不會(huì)被修改,可以看到下面的代碼,當(dāng)給 User 對(duì)象初始賦值 name:alice,age:18 時(shí),以后再賦值便無(wú)效了,以及每次返回都是初始的實(shí)例對(duì)象。

ES6中使用類(lèi)的靜態(tài)屬性
以上代碼使用ES6語(yǔ)法來(lái)實(shí)現(xiàn),通過(guò)類(lèi)的靜態(tài)屬性來(lái)保存唯一的實(shí)例對(duì)象。
class Singeton {
constructor(name,age){
if(!Singeton.instance){
this.name = name;
this.age = age;
Singeton.instance = this;
}
return Singeton.instance;
}
}創(chuàng)建方式仍然是一樣的,通過(guò) new 關(guān)鍵字創(chuàng)建類(lèi)的實(shí)例對(duì)象。
案例
那這樣一種設(shè)計(jì)模式在開(kāi)發(fā)中實(shí)際有什么用途呢?我們?cè)囅脒@樣一個(gè)業(yè)務(wù)場(chǎng)景:訪問(wèn)網(wǎng)站時(shí),很久沒(méi)有操作頁(yè)面,此時(shí)授權(quán)過(guò)期,當(dāng)我們點(diǎn)擊頁(yè)面上的任何一個(gè)地方,都會(huì)彈出一個(gè)登錄框。
那么這個(gè)登錄框,是全局唯一的,不會(huì)存在多份,也不會(huì)互相沖突,所以不需要每次都創(chuàng)建一份,保留初始那一份就夠了。
提前創(chuàng)建節(jié)點(diǎn)
我們可能會(huì)想到首先在頁(yè)面中提前創(chuàng)建節(jié)點(diǎn),編寫(xiě)好頁(yè)面樣式,最后通過(guò)控制元素的 display 屬性來(lái)達(dá)到顯示和隱藏的效果。
<div class="modal">登錄對(duì)話框</div>
<button id="open">打開(kāi)</button>
<button id="close">關(guān)閉</button>
<style>
.modal {
display: none;
/* 其他布局代碼省略 */
}
</style>
<script>
document.querySelector("#open").onclick = function(){
const modal = document.querySelector('.modal')
modal.style.display = 'block'
}
</script>這樣可以完成需求,全局只有一個(gè)登錄框,每次都展示同一個(gè)。但問(wèn)題是dom元素從一開(kāi)始它創(chuàng)建好并添加到body中,無(wú)論是否需要用到,如果有些場(chǎng)景不需要登陸,那么這里初始渲染就會(huì)浪費(fèi)空間。
單例模式
那如果不需要初始渲染,僅當(dāng)需要時(shí)才使用,并且每次都返回同一個(gè)實(shí)例的單例模式應(yīng)該如何實(shí)現(xiàn)呢?
我們可以這樣處理
<!-- 去除class為modal的標(biāo)簽,動(dòng)態(tài)創(chuàng)建 -->
<script>
const Modal = (function(){
let instance = null
return function(){
if(!instance){
instance = document.createElement("div")
instance.innerHTML = "登錄對(duì)話框"
instance.className = "modal"
instance.style.display = "none"
document.body.appendChild(instance)
}
return instance
}
})()
document.querySelector("#open").onclick = function(){
//創(chuàng)建modal,如果放在外面,一開(kāi)始就會(huì)創(chuàng)建元素
const modal = Modal()
//顯示modal
modal.style.display = "block"
}
document.querySelector("#close").onclick = function(){
const modal = Modal()
modal.style.display = "none"
}
</script>雖然上面的方式可以達(dá)到效果,但是創(chuàng)建對(duì)象和管理單例的邏輯都放在了對(duì)象內(nèi)部,是有些混亂的。并且如果下次需要?jiǎng)?chuàng)建頁(yè)面中唯一的 iframe,或者 script 標(biāo)簽,就得將以上函數(shù)照抄一遍。
通用單例
首先拆分函數(shù)邏輯,將執(zhí)行創(chuàng)建對(duì)象的邏輯拿出來(lái)
const createLayer = function(){
let div = document.createElement("div")
div.innerHTML = "登錄對(duì)話框"
div.className = "modal"
div.style.display = "none"
document.body.appendChild(div);
return div;
}
const Modal = (function(){
let instance = null
return function(){
if(!instance){
instance = createLayer()
}
return instance
}
})()以上修改后代碼邏輯就更為清晰,但此時(shí)還不支持通用化的創(chuàng)建別的組件,這時(shí)候我們想想如何將創(chuàng)建單例的方法進(jìn)行一些優(yōu)化,是否可以將單例需要執(zhí)行的函數(shù)抽象化。
const createSingle = (function(fn){
let instance;
return function(){
return instance || ( instance = fn.apply(this, arguments))
}
})()這樣改造后,如果存在創(chuàng)建 iframe 的方法,也可以直接使用。
const createIframe = function() {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
return iframe
}
const singleIframe = createSingle(createIframe)
document.querySelector("#open").onclick = function(){
const iframe = singleIframe()
iframe.style.display = 'block'
}實(shí)際應(yīng)用
以上都是咱小打小鬧的試用,那再來(lái)看看社區(qū)中一些非常棒的實(shí)現(xiàn)吧~ 比如:React 中常用的狀態(tài)管理工具 Redux 就使用到了單例模式,它有這樣一些要求。
- 單一數(shù)據(jù)源:整個(gè)應(yīng)用的 state 只存在于唯一一個(gè) store 中。
- State 是只讀的:不要直接改變 state 的值,唯一改變 state 的方法就是觸發(fā) action。
- reducer 是純函數(shù):需要編寫(xiě)純函數(shù) reducer 來(lái)修改 state 的值。
來(lái)看看 Redux 的源碼,為了便于閱讀已刪減部分邏輯判斷和注釋?zhuān)梢钥吹酵ㄟ^(guò) store 的 getState 方法每次獲取閉包中的 currentState。

單例模式在內(nèi)存中只有一個(gè)實(shí)例,可以減少內(nèi)存開(kāi)支,同時(shí)還能在系統(tǒng)設(shè)置全局的訪問(wèn)點(diǎn),優(yōu)化和共享資源。
以上就是單例模式的相關(guān)介紹。更多有關(guān) 前端、設(shè)計(jì)模式 的內(nèi)容請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- JavaScript事件發(fā)布/訂閱模式原理與用法分析
- JavaScript實(shí)現(xiàn)與使用發(fā)布/訂閱模式詳解
- JavaScript中發(fā)布/訂閱模式的簡(jiǎn)單實(shí)例
- JS前端設(shè)計(jì)模式之發(fā)布訂閱模式詳解
- js 發(fā)布訂閱模式的實(shí)例講解
- JavaScript設(shè)計(jì)模式之觀察者模式(發(fā)布訂閱模式)原理與實(shí)現(xiàn)方法示例
- JavaScript設(shè)計(jì)模式之觀察者模式與發(fā)布訂閱模式詳解
- 詳解JavaScript設(shè)計(jì)模式中的享元模式
- JavaScript 設(shè)計(jì)模式 安全沙箱模式
- JavaScript設(shè)計(jì)模式之觀察者模式(發(fā)布者-訂閱者模式)
- javascript 發(fā)布-訂閱模式 實(shí)例詳解
相關(guān)文章
微信小程序 視圖層(xx.xml)和邏輯層(xx.js)詳細(xì)介紹
這篇文章主要介紹了微信小程序 視圖層(xx.xml)和邏輯層(xx.js)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-10-10
Web?Animations?API實(shí)現(xiàn)一個(gè)精確計(jì)時(shí)的時(shí)鐘示例
這篇文章主要為大家介紹了Web?Animations?API實(shí)現(xiàn)一個(gè)精確計(jì)時(shí)的時(shí)鐘示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Thinkphp5微信小程序獲取用戶(hù)信息接口的實(shí)例詳解
這篇文章主要介紹了Thinkphp5微信小程序獲取用戶(hù)信息接口的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09

