JS 设计模式之单例模式

随着项目做的越来越多,项目越来越大,也越来越意识到设计模式的重要性,好的设计模式可以大幅简化项目的复杂度和耦合性,使编写、维护都变得轻松许多。

单例模式的定义是:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏 览器中的 window 对象等。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少 次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。
——《Javascript设计模式与开发实践》

书上都讲的很清楚了,但是,只是看一遍照着练一遍是没用的,把书合上,自己从头实现一遍,自己把该踩的坑再踩一遍,才能真正的学会。如果能讲清楚,那就真是学明白了。而且,人和人的思维方式是不一样的,所以就可能有人看书看不懂,结果看我的博客却看懂了,那便真是极好的~

正题

单例模式的核心是确保只有一个实例,并提供全局访问。

所以说,全局变量本身就是一个单例。但是,这样会污染全局作用域,不过这个理由并不充分,因为要全局访问必须要存在于全局作用域中啊~但还有一个问题,就是全局变量易被覆盖,所有我们就需要一个能随时创建出同一个对象的函数。

实现单例模式,简单来说,就是用一个变量来标记一下之前是否创建过要获取的对象,如果没有创建过,就创建一个新对象,并且把这个对象保存起来。否则,就直接返回那个对象。

可以将那个变量的初始值设为 null,然后将新创建的对象赋给它,这样判断它的真假就可以知道之前是否创建过对象,如果创建过,就直接返回它。既可以起到标记作用,也可以起到保存作用。

但是用一个全局变量来作为标记太不优雅,还会污染全局作用域,还不符合封装原则,并且,还会出现跟开头提到的同样的问题,这个时候就要请出闭包了~

工厂模式

var CreateObj = (function () {   //  我们把这个匿名函数称作 oldFn
    let obj = null;
    return () => {    //  我们把这个匿名函数称作 newFn
        if (obj) {
            return obj
        }
        obj = {
            text: "嘿嘿"
        }
        return obj
    }
})()

var o1 = CreateObj()
var o2 = CreateObj()

console.log(o1 === o2);
//  true

为了叙述方便,我在注释中为匿名函数命了名

通过立即执行函数表达式,CreateObj 变成了等号后面的那个函数的返回值——一个匿名函数 newFn,而这个函数会保存 oldFn 中的作用域,也就是说它会保存 obj 这个对象,这样每次调用时访问的就都是同一个 obj,于是,我们就可以通过这种方式使得每次调用 CreateObj() 获得的都是同一个对象。

上面的代码的 newFn 中也可以简写成如下形式

return obj || (obj = {text: "嘿嘿" })

构造函数模式

var Person = (function () {
    let obj = null
    return function () {
        if(obj) {
            return obj;
        }
        this.name = "老司机~��";
        obj = this;
    }
})()

var p1 = new Person()
var p2 = new Person()

console.log(p1);
//  { name: '老司机~��' }

console.log(p1 === p2);
//  true

构造函数模式和工厂模式没有什么区别,只是创建新对象的方式不同而已。

但是,还有一个问题,即这样不管是想把一个构造函数/工厂函数改为单例模式,还是改回来,都很复杂,能不能用一个通用的函数来处理呢?

通用的单例模式

//  单例化之前的工厂函数
function createObj(name) {
    return {
        name: name
    }
}


var getSingleFun = function (fn) {
    var obj = null;
    return function () {
        return obj || (obj = fn.apply(this, arguments));
    }
}

var createSingleObj = getSingleFun(createObj);

var o1 = createSingleObj('小明')
var o2 = createSingleObj('小马')

console.log(o1 === o2);
//  true

console.log(o1.name);
//  小明

console.log(o2.name);
//  小明

这个方法会比之前的更通用,向 getSingleFun 中传入任意函数,都可以返回一个新的函数,通过这个新的函数即可创建单例。

但是这会有一个问题,即只有第一次创建时的参数有效,之后创建时的参数将不起作用,但这需要看具体的问题,如果要求全局只有唯一一个目标对象,那么这样是没问题的。但如果像 Angular 一样,参数相同时返回同一个对象,参数不同时返回不同的对象,就需要在 getSingleFun 中创建新对象时记录下它的参数,再创建时检测是否有参数相同的实例,若有则直接返回。这里就先不写了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值