JavaScript设计模式之单例模式

351 阅读3分钟

单例模式的定义:保证一个类只有一个实例,并且向外提供一个访问它的全局访问点。

在JavaScript中,我们可能会使用全局变量来当作单例模式,比如let a = {},这当然可以,既满足了只有一个实例,又满足全局访问点,但是这样容易造成命名空间污染。在大型项目中如果不加管理,很容易出现很多这样变量,也很容易被不小心覆盖。 对于全局变量我们可以使用闭包封装私有变量:

	let user = (function(){
    	let _name = 'felix',
        	_age = 19;
        
        return {
            getUserInfo:functino (){
            	return _name + '-' + _age;
            	}
            }
       })()

我们约定私有变量_name和_age,她们被封装在闭包产生的作用域中,外部是访问不到这两个变量的,这样就避免了全局变量的污染。

惰性单例

惰性单例指的是只有在我们需要的时候才创建对象实例。惰性单例是单例模式的重点,因为他很好用!

下面代码展示的是:点击头像弹出登录窗口。

    let creatLoginLayer = (function(){
        let div;
        return function(){
            if(!div){
                div = document.creatElement('div');
                div.innerHTML = '我是登录弹窗';
                            div.style.display = 'none';
                            document.body.appendChild(div);
            }
            return div;
        }
    })()
    document.getElementById('loginId').onclick = function(){
        let loginLayer = creatLoginLayer();
        loginLayer.style.display = 'block';
    }

在这段代码中存在两个问题

  • 其实这段代码违反单一职责原则,创建对象和管理单例的逻辑都放在了一起。
  • 如果我们还要创建注册/或者其他元素,就要把这个逻辑复制一遍吗?我相信很多人都这样懒省事做过,比如看下面创建iframe的例子。
	var createIframe= (function(){
		var iframe;
		return function(){
			if ( !iframe){
				iframe= document.createElement( 'iframe' );
				iframe.style.display = 'none';
				document.body.appendChild( iframe);
			}
			return iframe;
		}
	})();

其实我们可以把不变的部分抽离出来。

	let obj;
	if(!obj){
		obj = xxx;
    }

现在我们就把刚才的例子中不变的抽出来。

	var getSingle = function( fn ){
		var result;
		return function(){
			return result || ( result = fn .apply(this, arguments ) );
		}
	};

接下来我们来实现创建登录浮窗。我们把登录浮窗的方法当作getSingle的参数fn传进去,我们不仅可以传入creatLoginLayer,还可以传入creatIframe、creaetScript等。之后再让getSingle返回一个函数,并用变量result保存fn的计算结果。result变量因为是在闭包中,他永远不会销毁,所以我们再次使用这个方法的时候,就直接返回result了。代码如下:

	var createLoginLayer = function(){
		var div = document.createElement( 'div' );
		div.innerHTML = '我是登录浮窗';
		div.style.display = 'none';
		document.body.appendChild( div );
		return div;
	};
	// 上面抽出来的逻辑
	var createSingleLoginLayer = getSingle( createLoginLayer );
    
	document.getElementById( 'loginBtn' ).onclick = function(){
		var loginLayer = createSingleLoginLayer();
		loginLayer.style.display = 'block';
	};

总结

在单例模式中,我们用到了闭包和高阶函数,也在实现单例模式的过程中,把控制实例只实现一个的方法和逻辑相拆分,也就是创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。

参考

JavaScript设计模式与开发实践