JavaScript设计模式之单例模式

单例模式

  单例模式的定义是:保证一个类仅有一个实例,并提供了一种访问其唯一的对象的方式。
  单例模式是一种常用的模式之一,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的 window 对象等。使用传统面向对象思想简单的实现一个单例模式:

var Singleton = function (name) {
	this.name = name;
}

Singleton.prototype.getName = function () {
	return this.name;
}

Singleton.getInstance = (function () {
	var instance = null;
	return function (name) {
		if(instance == null) {
			instance = new Singleton(name);
		}
		return instance;
	}
})();

var instance1 = Singleton.getInstance("xiaoming");
var instance2 = Singleton.getInstance("xiaohong");

console.log(instance1.getName());			// 打印 xiaoming
console.log(instance2.getName());			// 打印 xiaoming

  通过 Singleton.getInstance 来获取 Singleton 类的唯一对象,当第一次创建时,创建一个新的 Singleton 实例并且保存在内部,当第二次创建时,发现已存在实例直接返回之前创建好的实例,实现单例模式。

  我们也可以使用代理类的方式来实现单例模式,稍微改动一下代码:

var Singleton = function (name) {
	this.name = name;
}

Singleton.prototype.getName = function () {
	return this.name;
}
// 引入代理类
var ProxySingleton = (function () {
	var instance = null;
	return function (name) {
		if(instance == null) {
			instance = new Singleton(name);
		}
		return instance;
	}
})();

var instance1 = new ProxySingleton("xiaoming");
var instance2 = new ProxySingleton("xiaohong");

console.log(instance1.getName());			// 打印 xiaoming
console.log(instance2.getName());			// 打印 xiaoming

  虽然通过传统方法可以实现单例模式,但是 JavaScript 其实是不存在类的,也正因为如此,传统的单例模式实现在 JavaScript 中并不适用。但在 JavaScript 中,我们经常会把全局变量当成单例来使用。例如:

var a = {};

  当用这种方式创建对象 a 时,对象 a 确实是独一无二的。如果 a变量被声明在全局作用域下,则我们可以在代码中的任何位置使用这个变量,全局变量提供给全局访问是理所当然的。这样就满足了单例模式的两个条件(1.只能有一个实例;2.提供一个访问它的全局访问点)。

  前面我们已经知道了两种方式实现单例模式,接下来我们再了解另外一种----惰性单例。也就是在我们需要的时候才创建对象实例。

  例如我们要开发一个登录浮窗,这个浮窗在页面是要唯一的,不能同时出现两个登录浮窗,我们可以用一个变量来判断是否已经创建过登录浮窗,来实现浮窗唯一。代码示例:

var createLoginLayer = (function () {
	var div = null;
	return function () {
		if(div == null) {
			div = document.createElement('div');
			div.innerHTML = '我是登录浮窗';
			div.style.display = 'none';
			document.body.appendChild(div);
		}
		return div;
	}
})();

document.getElementById('loginBtn').onclick = function() {
	var loginLayer = createLoginLayer();
	loginLayer.style.display = 'block';
};

这段代码虽然实现了惰性单例,但是需求总是会增加的,如果我们要在页面增加一些其它标签的内容,比如 iframe,这时我们又要需要重新写一遍上面的代码:

var createIframe = (function () {
	var iframe= null;
	return function () {
		if(iframe== null) {
			iframe= document.createElement('iframe');
			iframe.innerHTML = '我是登录浮窗';
			iframe.style.dsiplay = 'none';
			document.body.appendChild(iframe);
		}
		return iframe;
	}
})();

这就违反了单一职责原则,把创建对象和管理单例的逻辑都放在了 createLoginLayer 对象内部。我们需要把不变的部分抽离出来,我们可以发现管理单例的逻辑始终都是一样的,可以把这部分代码抽象出来:

var obj;
return function(){
	if( obj == null ) {
		obj = xxx;				// 不相同部分
	}
	return obj;
} 

然后抽象出一个专门只为创建单一实例的一个函数:

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

接下来我们就只需要传入返回创建页面元素的函数,通过不同的创建函数作为参数。动态的增加不同的页面元素,改写之前创建登录浮窗的代码:

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 = createLoginLayer();
	loginLayer.style.display = 'block';
};

增加 iframe 或者其它元素跟此方法一样,只需重新写创建对应的页面元素的函数,然后调用 getSingle 创建对应的页面内容即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值