设计模式-单例

63 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

单例的核心是确保有且仅有一个实例,并提供一个访问它的全局访问点

单例,即单一实例,能保证何时何地访问该实例,永远都是指向同一个对象的实现模式,就是单例模式。

在javaScript语言中,每一个全局对象,从定义上来看都是一个单例;但我们不可能把所有对象都挂到全局对象下,这个时候,我们就需要通过其他方式实现单例模式并提供唯一访问入口。接着来看看javaScript下可以如何实现单例模式:

  • 闭包

先看代码:

const instanceInit = (function () {
    let instance;
    return function () {
        if (!instance) {
            instance = {
                name: 'instance',
                title: '我是一个单例',
            };
        }
        return instance;
    };
})();

const instance1 = instanceInit();
const instance2 = instanceInit();

console.log(instance1 === instance2); // true

我们通过闭包的形式,将instance当做局部变量保存在创建instanceInit的自执行匿名函数内,并将访问函数以返回值的方式赋值给instanceInit本身,于是我们可以通过调用它访问instance实例。闭包的存在,保证了我们的每次访问都能指向该变量。

class InstanceMode {
    static Instance = null;
    static getInstance() {
        if (InstanceMode.Instance) {
            return InstanceMode.Instance;
        }
        InstanceMode.Instance = new InstanceMode();
        return InstanceMode.Instance;
    }
    constructor() {
        this.name = 'instance';
        this.title = '我是一个单例';
    }
}

const instance1 = InstanceMode.getInstance();
const instance2 = InstanceMode.getInstance();

console.log(instance1 === instance2);

用类实现创建单例,简单来说就是利用类的静态属性,将类实例的创建与引用挂载到它的静态属性下,并提供一个获取方法,使用者通过调用该静态方法,完成实例的创建与获取,并保证后续的引用都指向该静态实例属性。

所以到这里,我们知道,单例的核心实现,是一次创建,后续访问始终指向该创建后的对象

// 核心实现
{
    if(instance) {
        return instance
    }
    instance = new CreateInstance()
    return instance
}
  • 惰性单例

这里再补充一种单例创建模式,惰性单例,即在需要的时候才创建对象实例,在上面的闭包函数和类中,我们都是将单例的创建和返回写到了一个函数内,那么,惰性单例下,就是将创建实例和管理单例分开维护。

function getSingle(fn) {
    let result;
    return function () {
        return result || (result = fn.apply(this, auguments));
    };
}

const createSingler = getSingle(createSingle); // createSingle创建对象

由于分开维护,那么我们可以很简单的复用getSingle函数给我们想要的对象创建函数实现各自的单例模式。

思考:既然有全局对象,为什么我们还需要用单例模式复刻一个闭包下的'全局对象'呢

全局对象容易造成命名空间污染,项目体量大了,很容易覆盖变量,导致难以追溯和定位的bug,减少全局变量的使用是有必要的

当你需要创建的变量需要保证二次访问指向同一个实例,那么就有单例模式的使用价值了。