彼此 發表於 2021-12-3 20:00:00

TypeScript 中的单例模式

<p>单例模式(单体):一个类有且只实例化一个实例对象</p>
<p>更具体的说:</p>
<pre><code class="language-markdown">如果一个类对外只提供一个对象实例,并且对外提供一个唯一可以访问该对象的方法或者属性,

那么这样就可以保证该对象的唯一性
</code></pre>
<p>那为啥不直接声明一个对象而是通过类实例化出一个对象?</p>
<p>显然是最大限度的利用面向对象的思想:更具<strong>封装性</strong>,更<strong>易于扩展</strong></p>
<h2 id="场景">场景</h2>
<p>何时使用单例模式呢?当然是从单例模式本身的重要特征考虑:<strong>唯一性</strong></p>
<p>唯一:独立无二,有且仅有一个。符合此特征就可以考虑使用单例模式。</p>
<p>例如:</p>
<p>场景一:客户端存储</p>
<pre><code class="language-markdown">localStore/sessionStory =&gt; 同源策略,同协议,同域名,同端口共用同一份数据 =&gt; 单例模式
</code></pre>
<p>场景二:全局状态管理</p>
<pre><code class="language-markdown">全局状态 store =&gt; SPA 共用全局属性和方法(唯一的实例对象) =&gt; 单例模式 (react-redux , vuex 等)
</code></pre>
<p>当然,还有很多场景~譬如模拟一个地球,模拟太阳系什么的</p>
<h2 id="模式">模式</h2>
<p>在 typescript 中如何实现单例模式呢?就以场景一中的客户端存储为例:</p>
<p>第一步:私有化构造器</p>
<pre><code class="language-typescript">class LocalStorageLayz {
// 私有化构造器
private constructor() {}
}
</code></pre>
<p>第二步:对外提供可访问的方法,调用该方法可以得到当前类的唯一对象实例</p>
<pre><code class="language-typescript">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));
}
}
</code></pre>
<p>第三步:外部调用第二步对外提供的方法,得到唯一实例</p>
<pre><code class="language-typescript">const instanceLayz = LocalStorageLayz.getInstance();

instanceLayz.setItem("instanceLayz", { "1": 1, "2": 3 });

let val = instanceLayz.getItem("instanceLayz");

console.log("inatanceLayz", val);
</code></pre>
<p>其中第二步中,可以通过对外提供的静态属性直接引用实例对象,也可以通过对外提供的静态方法去初始化静态属性。如下:</p>
<pre><code class="language-typescript">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));
}
}
</code></pre>
<p>区别在于,通过静态方法可以手动控制挂载唯一实例的时机会,而通过静态属性则会在类加载时挂载到静态属性上</p>
<pre><code class="language-typescript">const instance = LocalStorage.localStorage;
instance.setItem("instance", { "3": 3, "4": 4 });
const value = instance.getItem("instance");
console.log("instance", value);
</code></pre>
<h2 id="扩展">扩展</h2>
<p>JavaScript 实现</p>
<pre><code class="language-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
</code></pre>
<p>闭包实现</p>
<pre><code class="language-javascript">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
</code></pre>
<h2 id="总结">总结</h2>
<p>简单实现了通过 typescript 中的静态成员(静态方法/静态属性)实现 单例模式的具体逻辑:首先明确单例模式的的重要特征:单个实例;然后如何通过保证一个类有且只有一个实例呢?</p>
<ul>
<li>对外提供一个静态属性或者静态方法;</li>
<li>在类挂载的时候实例化单例或者通过提供的静态方法手动实例化单例</li>
</ul>
<p>也许你会疑惑为啥单例为啥不直接放到原型上去?而是放在类的静态成员上呢?这里前提是得知道类的静态成员以及 JavaScript 中的原型机制!</p>
<h2 id="references">references</h2>
<ul>
<li>https://github.com/shanejix/front-end-playground/tree/master/typescript/singleton-typescript</li>
</ul><br><br>
来源:https://www.cnblogs.com/shanejix/p/15639849.html
頁: [1]
查看完整版本: TypeScript 中的单例模式