mhahaha/Javascript-Design-Patterns

javascript设计模式 - 单例模式

mhahaha opened this issue · 0 comments

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

适用场景:全局只需要一个的对象时,例如线程池,全局缓存等

实现一个标准的单例模式

原理:获取一个实例时先判断该实例是否创建,若未创建,则先创建并缓存,然后返回实例;若已创建,则返回缓存中的实例。

示例代码:

const userInfo = null

const getUserInfo = (name, info) => {
    if (!userInfo) {
        userInfo = {
            name: name,
            info: info
        }
    }
    return userInfo
}

当使用这种方式创建一个仅有的实例时,我们是在全局作用域中新增全局变量,存在很多全局变量时,很容易造成变量污染,所以我们要么适当使用命名空间来控制全局变量数量,例如:

const userInfo = null

const namespace1 = {
    a: 1,
    b: 2
}

要么使用闭包来封装私有变量,仅暴露跟外界通信的接口:

const user  (() => {
    const name = 'dev'
    const age = 20

    return {
        getUserInfo: () => {
            return `${name} - ${age}`
        }
    }
})()

惰性单例

惰性单例在我们实际开发过程是很常用也很重要的技术,例如我们页面初始化时创建登录弹窗,我们常见的做法可能有以下几种:
1.静态直出直接添加弹窗,控制其显示及隐藏
2.需要登录时初始化,退出时remove
3.需要登录时初始化,退出时hide

可以比较三者的消耗,第三种显然是最合理的,我们进入一个页面只是想看一些附加功能,可能并不需要登录,也可能我们登录之后需要退出登录进行账号的切换...

惰性单例的核心就是对象被调用时才会被真正创建。

通用惰性单例

开发过程中,我们发现有一段代码可以复用时,我们当然是想直接copy过来,但此时如果让我们去熟悉里面的逻辑并做部分修改,我个人是比较排斥的。同样的道理,写单例模式时,我们也要尽量做到其通用性。

通用惰性单例 = 改变的业务逻辑 + 不变的单例模式框架

const getSingleton = fn => {
    let result

    return () => {
        return result || (result = fn.apply(this, argument))
    }
}

此时,任何业务都可以以参数的形式进行传递来完成一次单例模式的使用,例如:

const createLoginIframe = () => {
    const iframe = document.createElement('iframe')
    document.body.appendChild(iframe)

    return iframe
}

const loginIframe = getSingleton(createLoginIframe)

在这个例子中可以看出,职责比较分明,创建实例对象的职责和管理单例的职责放置在两个方法里面,没有业务耦合,完成了单例模式的通用性。