浅析JavaScript设计模式——单例模式

来源:互联网 发布:软件的license 编辑:程序博客网 时间:2024/06/06 03:53

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

举一个通俗的例子,在页面中点击登录按钮,弹出了一个登录浮窗,这个登录浮窗是唯一的,无论我们单击多少次,浮窗只会创建一次

其实我们可能无意中都会使用过单例模式,我们的做法往往都是使用一个变量来标志当前是否已经为某个类创建了对象,
如果true,那么下一次再想获得这个类的实例时,直接返回之前创建过的对象

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


其实在JavaScript中,单例模式并没有这么复杂
因为我们没有类
既然我们只要一个唯一的对象,为什么要创建“类”呢

var a = {};

我们这样创建了对象a,它确实独一无二
而且满足了单例模式的两个条件

  • 一个实例
  • 全局访问

但是全局变量很容易造成命名空间污染
如果项目很大的话,不小心覆盖了变量那就是致命的


所以
在详细讲解这个单例模式之前,我们先来讨论这样一个问题,怎样降低全局污染?
全局污染也就是变量大量存在于全局作用域污染了全局空间
降低全局污染有两种办法

  • 使用命名空间
var namespace_payen = {    a: function(){        //...    }    b: function(){        //...    }}

适当使用命名空间,并不会杜绝全局变量,但是可以减少全局变量的数量

  • 使用闭包封装私有变量
var payen = (function(){    var _name = 'payson.Tsung',        _age = 19;    return {        getInfo: function(){            return _name + ' ' + _age;        }    }})();

变量被封装在了闭包内,只暴露一些接口用于外部通信,从而避免了对全局的命令污染


下面我来谈谈这个单例模式
先来个简单的例子
下面我声明了一个函数,每次调用都创建一个小方块

function createDiv(){    var div = document.createElement('div');    div.style.width = '100px';    div.style.height = '100px';    div.style.background = 'red';    div.style.marginBottom = '10px';    document.body.appendChild(div);}createDiv();createDiv();createDiv();

调用了三次,页面出现了三个小方块

下面我就使用单例模式,让它只创建一个div
普通的使用bool变量的方法会污染全局空间这里就不讨论了╰( ̄▽ ̄)╭

var createDiv = (function(){    var div;    return function(){        if(!div){            div = document.createElement('div');            div.style.width = '100px';            div.style.height = '100px';            div.style.background = 'red';            div.style.marginBottom = '10px';            document.body.appendChild(div);        }    }})();createDiv();createDiv();createDiv();

再来看看页面

只有一个小方块
不知道大家看没看懂这几行代码

div声明在了立即执行函数中作为私有变量
没有执行函数前, div值为undefined
第一次执行函数时,判断div有没有,没有于是创建了一个DOM节点并且插入到了文档
随后再执行函数时,div变量已经缓存了刚刚创建的DOM节点,于是不再创建
无论执行几次,小方块只会创建一次
这就是单例模式,而且是一个惰性单例

惰性单例就是在需要的时候才创建对象实例,而非在页面加载时就创建
这样做的好处大家都知道


虽然我们完成了惰性单例,但是我们同样发现了问题

  • 违反了单一职责原则,创建对象和管理单例放在了一个函数中createDiv
  • 如果我们还想创建一个其他的唯一对象,那就只能copy了

综上,我们需要
把不变的部分隔离出来,把可变的封装起来,这给予了我们扩展程序的能力,符合开放-封闭原则

下面我们就抽出管理单例的逻辑
无论怎样抽取,万变不离其中,用一个变量来标志是否创建过对象

var getSingle = function(fn){    var result;    return function(){        return result || (result = fn.apply(this, arguments));    }};var createDiv = function(){    var div = document.createElement('div');    div.style.width = '100px';    div.style.height = '100px';    div.style.background = 'red';    div.style.marginBottom = '10px';    document.body.appendChild(div);    return div;};var createSingleDiv = getSingle(createDiv);createSingleDiv();createSingleDiv();createSingleDiv();

创建的DOM节点保存在了result中
result变量因为自身在闭包中,不会被销毁
再将来的请求中,如果result已经被赋值了,那么它将返回这个值
相信大家可以看懂这几行代码

大功告成
单例模式很简单,而且也十分实用,他不仅仅用于创建对象,很有很多其他用途
比如说只绑定一次事件啦之类的


这个惰性单例模式创建对象和管理单例的职责分布在两个不同的方法,很棒
它并不难而且是非常实用的模式

==主页传送门==

4 0