Javascript 设计模式 - 单例模式

1,174 阅读2分钟

单例模式的定义是: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏 览器中的 window 对象等。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

实现

简单实现

/**
 * 单例模式
 */
class Singleton {
    constructor(name) {
        this.name = name;
        this.instance = null;
    }
    getName(){
        console.log(this.name);
    }
    static getInstance(name){
        if (!this.instance){
            this.instance = new Singleton(name);
        }
        return this.instance;
    };
}
const a = Singleton.getInstance('sven1');
const b = Singleton.getInstance('sven2');
console.log(a === b); // true

透明的单例模式

但是这个类需要实例化后调用方法才能创建实例,我们希望直接实例化时就能创建。

/**
 * 使用闭包实现单例模式
 */
const Singleton = (function(){
    let instance;
    class SingletonOrigin {
        constructor(name) {
            if (instance) {
                return instance;
            }
            this.name = name;
            instance = this;
            return this;
        }
        getName(){
            console.log(this.name);
        }
    }
    return SingletonOrigin;
})();

const a = new Singleton('sven1');
const b = new Singleton('sven2');
console.log(a === b); // true

为了把 instance 封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回 真正的 Singleton 构造方法,这增加了一些程序的复杂度,阅读起来也不是很舒服。

使用代理实现单例模式

/**
 * 使用代理实现单例模式
 */
const Singleton = (function (){
    class SingletonOrigin {
        constructor(name) {
            this.name = name;
        }
        getName(){
            console.log(this.name);
        }
    }
    let instance;
    return function(name) {
        if (!instance){
            instance = new SingletonOrigin(name);
        }
        return instance;
    }
})();

const a = new Singleton('sven1');
const b = new Singleton('sven2');
console.log(a === b); // true

惰性单例模式

前面几种实现方式是基于面向对象思路的实现,现在使用js特殊的方式实现单例模式,名为惰性单例模式

惰性单例模式可以推迟创建对象的时机,并不在一开始就创建,所以叫惰性

/**
 * 惰性单例模式
 */
const getInstance = (function () {
    function createInstance(name) {
        return {
            name: name,
            getName() {
                console.log(this.name);
            }
        }
    }
    function getSingle (fn){
        let result;
        return function() {
            if (!result) {
                result = fn.apply(this, arguments)
            }
            return result;
        }
    };
    return getSingle(createInstance);
})();


const a = getInstance('sven1');
const b = getInstance('sven2');
console.log(a === b); // true