JavaScript设计模式(一):单例模式

单例模式

单例模式保证一个类仅有一个实例,并且这一实例可被全局访问。
JS中并没有类的概念,最简单的单例模式就是声明全局变量,而为了减少全局变量的数量避免命名污染,我们常常使用以下两种方式实现单例。

1. 命名空间

使用对象字面量划分命名空间减少了全局变量的数量。

var Lean = {
    event: {
        addEvent: function(){ }
    },
    dom: {
        ...
    }
}

2. 闭包

使用闭包暴露特定接口供外部访问。

var Lean = function(){
    //私有属性
    var name = 'zz';
    return{
        //公有方法
        getName:function(){
            return 'name:' + name;
        }
    }
}

惰性单例

惰性单例是仅在需要的时候才创建对象实例。不像前面的单例模式,页面一旦加载,单例对象就会被创建。

1. 弹框/弹出层

当我们需要一个点击弹框效果时,一般思路是先创建好这个弹框,再通过按钮注册点击事件来实现弹框的显示与隐藏。

但这种方式有一个缺陷,如果用户没有点击这一按钮,那么为弹框而创建的DOM节点都是白白浪费的。

这时候惰性单例模式就派上用场了。我们使用一个变量来判断当前是否创建过弹框。
var createPopup = (function(){
    var pop;
    return function(){
        if(!pop){
            pop = document.createElement('div');
            pop.innerHTML = 'popup';
            pop.style.display = 'none';
            document.body.appendChild(pop);
        }
        return pop;
    }
})();

btn.onclick = function(){
    var oDiv = createPopup();
    oDiv.style.display = 'block';
};

2. 封装

当我们需要创建页面上唯一的iframe或者利用JSONP创建script标签来跨域请求数据时等许多场景都需要用到惰性单例模式,我们可以将其中的固定逻辑抽离出来封装成一个函数以复用。

var createSingle = function(fn){
    var res;
    return function(){
        return res || (res = fn.apply(this, arguments));
    }
};
利用闭包特性,函数中的res变量被留在内存中不会被销毁。因此可得知单例对象当前是否已经被创建。

3. 实践

现在我们来利用单例模式做一个上述弹框效果。

HTML


<body>
    <button id="alert">Alert</button>
    <button id="confirm">Confirm</button>
    <!--<div class="wrap">
        <div class="box">
            <h3 class="title">提示</h3>
            <div class="close">✖</div>
            <p class="msg"></p>
            <a class="ok" href="javascript:;">确定</a>
        </div>
    </div>-->
</body>

JavaScript

//类名选择器
var $ = function(sClass) {
    return document.getElementsByClassName(sClass)[0];
}

//参数:弹框类型 标题 内容 回调函数
var Popup = function(sType, sTitle, sMsg, fn) {
    
    var oWrap = document.createElement('div');
    document.body.appendChild(oWrap);
    
    oWrap.className = 'popup-wrap';
    oWrap.style.display = 'none';
    
    //根据参数确定弹框类型
    if (sType == 'alert') {
        oWrap.innerHTML = '...';
    } else if (sType == 'confirm') {
        oWrap.innerHTML = '...';
    }
    
    return oWrap;
}

//惰性单例逻辑
var createSingle = function(fn){
    var res;
    return function(){
        return res || (res = fn.apply(this, arguments));
    }
};

var createSinglePop = createSingle(Popup);

var oAlert = document.getElementById('alert');
var oConf = document.getElementById('confirm');

//注册click事件
oAlert.onclick = function(){
	var o = createSinglePop('alert', 'Alert Title', 'Hello World');
	o.style.display = 'block';
}
oConf.onclick = function(){
	var c = createSinglePop('confirm', 'Confirm Title', 'This is confirm content', () => alert('callback'));
	c.style.display = 'block';
}

JS Bin 查看效果

posted @ 2017-09-25 19:26  tt273z  阅读(336)  评论(0编辑  收藏  举报