單例模式(單體):一個類有且只實例化一個實例對象
更具體的說:
如果一個類對外只提供一個對象實例,並且對外提供一個唯一可以訪問該對象的方法或者屬性,
那么這樣就可以保證該對象的唯一性
那為啥不直接聲明一個對象而是通過類實例化出一個對象?
顯然是最大限度的利用面向對象的思想:更具封裝性,更易於擴展
場景
何時使用單例模式呢?當然是從單例模式本身的重要特征考慮:唯一性
唯一:獨立無二,有且僅有一個。符合此特征就可以考慮使用單例模式。
例如:
場景一:客戶端存儲
localStore/sessionStory => 同源策略,同協議,同域名,同端口共用同一份數據 => 單例模式
場景二:全局狀態管理
全局狀態 store => SPA 共用全局屬性和方法(唯一的實例對象) => 單例模式 (react-redux , vuex 等)
當然,還有很多場景~譬如模擬一個地球,模擬太陽系什么的
模式
在 typescript 中如何實現單例模式呢?就以場景一中的客戶端存儲為例:
第一步:私有化構造器
class LocalStorageLayz {
// 私有化構造器
private constructor() {}
}
第二步:對外提供可訪問的方法,調用該方法可以得到當前類的唯一對象實例
class LocalStorageLayz {
// 靜態屬性 引用 LocalStotrage 類的唯一實例對象
static localStorage: LocalStorageLayz;
// 私有化構造器
private constructor() {}
// 提供一個外部可訪問的的靜態方法
public static getInstance() {
if (!this.localStorage) {
this.localStorage = new LocalStorageLayz();
}
return this.localStorage;
}
// 實例方法
public getItem(key: string) {
let val = localStorage.getItem(key);
return val !== null ? JSON.parse(val) : null;
}
// 實例方法
public setItem(key: string, val: any) {
localStorage.setItem(key, JSON.stringify(val));
}
}
第三步:外部調用第二步對外提供的方法,得到唯一實例
const instanceLayz = LocalStorageLayz.getInstance();
instanceLayz.setItem("instanceLayz", { "1": 1, "2": 3 });
let val = instanceLayz.getItem("instanceLayz");
console.log("inatanceLayz", val);
其中第二步中,可以通過對外提供的靜態屬性直接引用實例對象,也可以通過對外提供的靜態方法去初始化靜態屬性。如下:
class LocalStorage {
// 靜態屬性 引用 LocalStotrage 類的唯一實例對象
static localStorage: LocalStorage = new LocalStorage();
// 私有化構造器
private constructor() {}
// 實例方法
public getItem(key: string) {
let val = localStorage.getItem(key);
return val !== null ? JSON.parse(val) : null;
}
// 實例方法
public setItem(key: string, val: any) {
localStorage.setItem(key, JSON.stringify(val));
}
}
區別在於,通過靜態方法可以手動控制掛載唯一實例的時機會,而通過靜態屬性則會在類加載時掛載到靜態屬性上
const instance = LocalStorage.localStorage;
instance.setItem("instance", { "3": 3, "4": 4 });
const value = instance.getItem("instance");
console.log("instance", value);
擴展
JavaScript 實現
function Singleton() {}
Singleton.getInstance = function (...args) {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
};
Singleton.instance = null;
const a = Singleton.getInstance();
const b = Singleton.getInstance();
console.log(a === b); // true
閉包實現
const Singleton = (function () {
let instance = null;
return function () {
if (instance) {
return instance;
}
return (instance = this);
};
})();
const a = new Singleton();
const b = new Singleton();
console.log(a === b); // true
總結
簡單實現了通過 typescript 中的靜態成員(靜態方法/靜態屬性)實現 單例模式的具體邏輯:首先明確單例模式的的重要特征:單個實例;然后如何通過保證一個類有且只有一個實例呢?
- 對外提供一個靜態屬性或者靜態方法;
- 在類掛載的時候實例化單例或者通過提供的靜態方法手動實例化單例
也許你會疑惑為啥單例為啥不直接放到原型上去?而是放在類的靜態成員上呢?這里前提是得知道類的靜態成員以及 JavaScript 中的原型機制!
references
作者:shanejix
出處:https://www.shanejix.com/posts/TypeScript 中的單例模式/
版權:本作品采用「署名-非商業性使用-相同方式共享 4.0 國際」許可協議進行許可。
聲明:轉載請注明出處!