《设计模式》 - 1. 单例模式( Singleton )

Javascript设计模式 - 原文链接

单例模式 :

语言 : JavaScript

定义 :

单例模式有两个要点,保证一个类只有一个实例,并提供访问该实例的全局访问点。

说明 :

这篇文章通过一个简单的创建Mask的需求 , 一步步优化代码中 , 循序渐进 , 通俗易懂地讲解了单例的产生以及运用 , 加上了一些个人理解 .

假定需求 : 在点击按钮需要弹出一个遮罩层的时 . (例如 web.qq.com点击登录的时候) 
这里写图片描述


第一次处理 :

创建 div :

var createMask = function(){

   return document.body.appendChild(document.createElement(div));

}

给按钮添加点击回调方法 :

$('button').click(function(){

   var mask  = createMask();
   mask.show();

})

现在的处理虽然可以解决需求, 但是每次点击按钮的时候都创建出一个新的div, 这很显然不是很好的解决办法


进行改进 :

创建一个mask变量, 只创建一次div, 每次需要显示的时候, 只需要调用show();方法即可

var mask = document.body.appendChild(document.createElement('div'));

$('button').click(function(){

   mask.show();

})

这样处理虽然表面上可以减少创建 和 移除 div 的次数, 可是如果我们从始至终都不需要使用这个div 遮罩呢? 那不是白白浪费了这个div, 对dom节点的任何操作都应该非常吝啬, 显然我们需要再次改进.


再次改进 :

我们借助一个变量来判断是否创建过这个div

var mask;

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

这样处理看起来就好很多了, 完成了一个基本的单例, 既不会有多余的创建, 也不会白白创建一个不需要的div.

但是, 如果仔细研究这个函数, 还是能发现其中的问题, 函数体内改变了外界变量mask的引用, 在多人协作的项目中, createMask是个不安全的函数. 另一方面, mask这个全局变量并不是非需不可.


第三次改进 :

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

用了一个闭包把变量mask包起来, 使得这个函数是一个封闭的函数.


原文中提到, 在js中函数是第一型, 意味着函数也可以当参数传递. 
它只能用于创建遮罩层. 假如我又需要写一个函数, 用来创建一个唯一的xhr对象呢? 能不能找到一个通用的singleton包装器.

单例模式最终代码 :

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的函数式特性还不足以完全的消除声明和语句.

猜你喜欢

转载自blog.csdn.net/cleve_baby/article/details/80941860