深入了解JavaScript设计模式系列-单例模式

本文探讨JavaScript中实现单例模式的方法,解决全局唯一实例的创建问题,通过闭包和高阶函数实现更安全、灵活的单例对象生成,避免重复创建资源。

前言

单例模式的定义是产生一个类的唯一实例,但js本身是一种“无类”语言。很多讲js设计模式的文章把{}当成一个单例来使用也勉强说得通。因为js生成对象的方式有很多种,我们来看下另一种更有意义的单例。

有这样一个常见的需求,点击某个按钮的时候需要在页面弹出一个遮罩层。

这个生成灰色背景遮罩层的代码是很好写的。

var createMask = function(){
   return document.body.appendChild(document.createElement(div));
}
复制代码
$('button').click( function(){
   Var mask  = createMask();
   mask.show();
})
复制代码

问题是, 这个遮罩层是全局唯一的, 那么每次调用createMask都会创建一个新的div, 虽然可以在隐藏遮罩层的把它remove掉. 但显然这样做不合理。

再看下第二种方案, 在页面的一开始就创建好这个div. 然后用一个变量引用它。

var mask = document.body.appendChild(document.createElement('div'));
$('button').click( function(){
   mask.show();
})
复制代码

这样确实在页面只会创建一个遮罩层div, 但是另外一个问题随之而来, 也许我们永远都不需要这个遮罩层, 那又浪费掉一个div, 对dom节点的任何操作都应该非常吝啬。

如果可以借助一个变量. 来判断是否已经创建过div呢?

var mask;
var createMask = function(){
    if (mask){
        return mask;
    } 
    else{
        mask = document,body.appendChild(document.createElement(div));
        return mask;
    }
}
复制代码

看起来不错, 到这里的确完成了一个产生单列对象的函数. 我们再仔细看这段代码有什么不妥。

首先这个函数是存在一定副作用的, 函数体内改变了外界变量mask的引用, 在多人协作的项目中, createMask是个不安全的函数. 另一方面, mask这个全局变量并不是非需不可. 再来改进一下。

var createMask = function(){
  var mask;
  return function(){
       return mask || (mask = document.body.appendChild(document.createElement('div')))
  }
}()
复制代码

用了个简单的闭包把变量mask包起来, 至少对于createMask函数来讲, 它是封闭的。

可能看到这里, 会觉得单例模式也太简单了. 的确一些设计模式都是非常简单的, 即使从没关注过设计模式的概念, 在平时的代码中也不知不觉用到了一些设计模式。 就像多年前我明白老汉推车是什么回事的时候也想过尼玛原来这就是老汉推车。

再回来正题, 前面那个单例还是有缺点. 它只能用于创建遮罩层. 假如我又需要写一个函数, 用来创建一个唯一的xhr对象呢? 能不能找到一个通用的singleton包装器。

js中函数是第一型, 意味着函数也可以当参数传递. 看看最终的代码。

var singleton = function(fn){
    var result;
    return function(){
        return result || (result = fn.apply(this, arguments));
    }
}
var createMask = singleton(function(){
    return document.body.appendChild(document.createElement('div'));
})
复制代码

用一个变量来保存第一次的返回值, 如果它已经被赋值过, 那么在以后的调用中优先返回该变量。

而真正创建遮罩层的代码是通过回调函数的方式传人到singleton包装器中的。这种方式其实叫桥接模式。 关于桥接模式, 放在后面一点点来说。

然而singleton函数也不是完美的, 它始终还是需要一个变量result来寄存div的引用。 遗憾的是js的函数式特性还不足以完全的消除声明和语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值