通用惰性单例模式

本文不仅仅是对于单例模式的总结,在最后还有对通用惰性单例的探讨和完善,不想看单例模式基础总结的可以直接跳转到「通用的单例中间类」来看我对于网络上流传的通用惰性单例模式的想法

单例模式

实现思路

用一个变量来标记当前的类是否创建过对象,如果没创建过,返回创建之后的实例,如果之前已经创建过,则返回之前创建过的实例

单例模式「不透明」

// 构造函数
function Singleton(name) {
    this.name = name
    this.instance = null
}
// 构造函数方法
Singleton.prototype.getName = () => {
    console.info(this.name)
}
// 单例控制
Singleton.getInstance = (name) => {
    if(!this.instance) {
        this.instance = new Singleton(name)
    }
    return this.instance
}

const a = Singleton.getInstance('a')
const b = Singleton.getInstance('b')

console.info(a, b) // true
复制代码

也可以使用闭包

// 构造函数
function Singleton(name) {
    this.name = name
}
// 构造函数方法
Singleton.prototype.getName = function() {
    console.info(this.name)
}
// 获取类的实例
Singleton.getInstance = (function() {
    let instance = null
    return function(name) {
        if(!instance) {
            instance = new Singleton(name)
        }
        return instance
    }        
})()

// 获取对象1
var a = Singleton.getInstance('a')
// 获取对象2
var b = Singleton.getInstance('b')
// 进行比较
console.info(a, b)
复制代码

当然也可以使用 ES6 的 class

class Singleton {
    constructor(name) {
        this.name = name
    }

    getName() {
        console.info(this.name)
    }

    getInstance = (function(){
        let instance = null
        return function(name) {
            if(!instance) {
                instance = new Singleton(name)
            }
            return instance
        }   
    })()
}

// 获取对象1
var a = Singleton.getInstance('a')
// 获取对象2
var b = Singleton.getInstance('b')
// 进行比较
console.info(a, b)
复制代码

但是此方法必须要使用 Singleton.getInstance() 才能获取到想要的实例,这与我们习以为常的 new 关键字获取实例对象有很大区别。其实这种方法的实现思路就是用构造函数中的一个方法去控制此构造函数生成的实例为单例,既然我们想要用 new关键字来生成单例,那我们为何不用另一个构造函数来控制此构造函数的创建呢

单例模式「透明」

class Person {
    constructor(name) {
        this.name = name
    }
    getName() {
        return this.name
    }
}

const Singleton = (function() {
    let instance
    return function(name) {
        if(!instance) {
            instance = new Person(name)
        }
        return instance
    }
})()


// 获取对象1
let a = new Singleton('a')
let b = new Singleton('b')
console.info(a === b) // true

复制代码

以上中间控制方法或类中的构造函数都是固定的,也就是说我们需要为不同的构造函数编写不同的中间方法或类,这样显然是不合理的。

通用的单例中间类

我发现现在流传在网上的基本上是如下这种

// 获取单独的实例
var singleton = function(fn) {
    var instance;
    return function() {
        return instance || (instance = fn.apply(this, arguments));
    }
}

// 创建遮罩层
var createMask = function(){
    // 创建div元素
    var mask = document.createElement('div');
    // 设置样式
    mask.style.position = 'fixed';
    mask.style.top = '0';
    mask.style.right = '0';
    mask.style.bottom = '0';
    mask.style.left = '0';
    mask.style.opacity = 'o.75';
    mask.style.backgroundColor = '#000';
    mask.style.display = 'none';
    mask.style.zIndex = '98';
    document.body.appendChild(mask);
    // 单击隐藏遮罩层
    mask.onclick = function(){
        this.style.display = 'none';
    }
    return mask;
};

// 调用
var oMask = singleton(createMask)()
var eMask = singleton(createMask)()
// 但是 oMask 与 eMask 不相等,获取 eMask 时其实也创建了新的 div
复制代码

这种调用方法其实仅仅是获取到了 createMask 中的返回值,每次 singleton() 其实都会执行 var instance ,所以对 instance 的判断其实是没用的。正确调用我觉得应该如下

const maskSingleton = singleton(createMask) // 先用一个变量接一下
const oMask = maskSingleton()
const eMask = maskSingleton() // 此时返回的还是之前已经创建的 mask
复制代码

但其实上面的例子并未很好的体现出通用惰性单例模式的作用,而且oMask 其实是获取不到 createMask 原型链上的方法的「也可能原本作者就不想获取 = =」,但我们之前的举例都是使用构造函数的举例,所以接下来我也会用构造函数的例子来展现。

const singleton = function(Fn) {
    var instance
    return function() {
        return instance || (instance = new Fn(...arguments))
    }
}

const Person = function(name){
    this.name = name
}

Person.prototype.getName = function() {
    return this.name
}

const Animal = function(name) {
    this.name = name
}

Animal.prototype.getName = function() {
    return this.name
}

const PersonSingleton = singleton(Person)
const liSi = PersonSingleton('liSi')
const zhangSan = PersonSingleton('zhangSan')

const AnimalSingleton = singleton(Animal)
const cat = AnimalSingleton('cat')
const dog = AnimalSingleton('dog')

console.info(zhangSan.getName()) // 'liSi'
console.info(dog.getName()) // 'cat'
复制代码

参考链接: segmentfault.com/a/119000001…

转载于:https://juejin.im/post/5cff2742e51d455071250aed

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值