一象颖、概念
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點姆钉,這樣的模式就叫做單例模式说订。
單例模式是設(shè)計模式中相對較為容易理解、容易上手的一種模式潮瓶,同時因為其具有廣泛的應(yīng)用場景陶冷,也是面試題里的常客毯辅。
二埂伦、實現(xiàn)思路
現(xiàn)在我們先不考慮單例模式的應(yīng)用場景,單看它的實現(xiàn)思恐,思考這樣一個問題:如何才能保證一個類僅有一個實例沾谜? 一般情況下膊毁,當(dāng)我們創(chuàng)建了一個類(本質(zhì)是構(gòu)造函數(shù))后,可以通過new關(guān)鍵字調(diào)用構(gòu)造函數(shù)進而生成任意多的實例對象基跑。像這樣:
class SingleDog {
show() {
console.log("我是一個單例對象");
}
}
const s1 = new SingleDog();
const s2 = new SingleDog();
// false
s1 === s2
我們先 new 了一個 s1婚温,又 new 了一個 s2,很明顯 s1 和 s2 之間沒有任何瓜葛媳否,兩者是相互獨立的對象栅螟,各占一塊內(nèi)存空間。而單例模式想要做到的是篱竭,不管我們嘗試去創(chuàng)建多少次力图,它都只給你返回第一次所創(chuàng)建的那唯一的一個實例。
要做到這一點掺逼,就需要構(gòu)造函數(shù)具備判斷自己是否已經(jīng)創(chuàng)建過一個實例的能力吃媒。我們現(xiàn)在把這段判斷邏輯寫成一個靜態(tài)方法(其實也可以直接寫入構(gòu)造函數(shù)的函數(shù)體里):
三、簡單demo
class SingleDog {
show() {
console.log("我是一個單例對象");
}
static getInstance() {
// 判斷是否已經(jīng)new過1個實例
if (!SingleDog.instance) {
// 若這個唯一的實例不存在吕喘,那么先創(chuàng)建它
SingleDog.instance = new SingleDog();
}
// 如果這個唯一的實例已經(jīng)存在赘那,則直接返回
return SingleDog.instance;
}
}
const s1 = SingleDog.getInstance()
const s2 = SingleDog.getInstance()
除了以上這種實現(xiàn)方法,也可以利用閉包來實現(xiàn):
class SingleDog {
show() {
console.log("我是一個單例對象")
}
}
SingleDog.getInstance = (function () {
// 定義自由變量instance兽泄,模擬私有變量
let instance = null
return function () {
// 判斷自由變量是否為null
if (!instance) {
// 如果為null則new出唯一實例
instance = new SingleDog();
}
return instance;
}
})()
const res = SingleDog.getInstance()
console.log(res.show())
四漓概、擴展
1.Vuex中的單例模式
Vuex 使用單一狀態(tài)樹漾月,用一個對象就包含了全部的應(yīng)用層級狀態(tài)病梢。至此它便作為一個“唯一數(shù)據(jù)源 (SSOT)”而存在。這也意味著梁肿,每個應(yīng)用將僅僅包含一個 store 實例蜓陌。單一狀態(tài)樹讓我們能夠直接地定位任一特定的狀態(tài)片段,在調(diào)試的過程中也能輕易地取得整個當(dāng)前應(yīng)用狀態(tài)的快照吩蔑。 ——Vuex官方文檔
在Vue中钮热,組件之間是獨立的,組件間通信最常用的辦法是 props(限于父組件和子組件之間的通信)烛芬,稍微復(fù)雜一點的(比如兄弟組件間的通信)我們通過自己實現(xiàn)簡單的事件監(jiān)聽函數(shù)也能解決掉隧期。
但當(dāng)組件非常多、組件間關(guān)系復(fù)雜赘娄、且嵌套層級很深的時候仆潮,這種原始的通信方式會使我們的邏輯變得復(fù)雜難以維護。這時最好的做法是將共享的數(shù)據(jù)抽出來遣臼、放在全局性置,供組件們按照一定的的規(guī)則去存取數(shù)據(jù),保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化揍堰。于是便有了 Vuex鹏浅,這個用來存放共享數(shù)據(jù)的唯一數(shù)據(jù)源嗅义,就是 Store。
Vuex如何確保Store的唯一性
我們先來看看如何在項目中引入 Vuex:
// 安裝vuex插件
Vue.use(Vuex);
// 將store注入到Vue實例中
new Vue({
el: "#app",
store,
})
通過調(diào)用Vue.use()方法隐砸,我們安裝了 Vuex 插件之碗。Vuex 插件是一個對象,它在內(nèi)部實現(xiàn)了一個 install 方法凰萨,這個方法會在插件安裝時被調(diào)用继控,從而把 Store 注入到Vue實例里去。也就是說每 install 一次胖眷,都會嘗試給 Vue 實例注入一個 Store
在 install 方法里武通,有一段邏輯和我們樓上的 getInstance 非常相似的邏輯:
let Vue // 這個Vue的作用和樓上的instance作用一樣
...
export function install (_Vue) {
// 判斷傳入的Vue實例對象是否已經(jīng)被install過Vuex插件(是否有了唯一的state)
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
// 若沒有,則為這個Vue實例對象install一個唯一的Vuex
Vue = _Vue
// 將Vuex的初始化邏輯寫進Vue的鉤子函數(shù)里
applyMixin(Vue)
}
以上便是 Vuex 源碼中單例模式的實現(xiàn)辦法了珊搀,套路可以說和我們的getInstance如出一轍冶忱。通過這種方式,可以保證一個 Vue 實例(即一個 Vue 應(yīng)用)只會被 install 一次 Vuex 插件境析,所以每個 Vue 實例只會擁有一個全局的 Store囚枪。