本文基于《JavaScript设计模式与开发实践》一书,用一些例子总结一下JS常见的设计你模式与实现方法。 定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现代理模式的单例
首先创建一个CreateDiv的构造函数 只负责创建div
var CreateDiv = function(html) {
this.html = html;
this.init();
}
CreateDiv.prototype.init = function(){
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div)
}
接下来引入代理类proxySingletonCreateDiv
var ProxysingletonCreateDiv = (function(){
var instance;
return function(html) {
if (!instance) {
instance = new CreateDiv(html)
}
return instance
}
})()
var a = new ProxysingletonCreateDiv('test1');
var b = new ProxysingletonCreateDiv('test2');
console.log(a === b) // true
通用的惰性单例
惰性单例是指在需要的时候才创建对象实例, 而不像之前的代码那样,利用自执行行数在代码执行时就把对象实例创建。
当我们点击登录按钮时,页面中可能出现一个弹框,而这个弹框是唯一的,无论点击多少次登录按钮,弹框只会被创建一次,那么这种情况下就适合用单例模式来创建弹框。
<html>
<body>
<button id="loginBtn">登录</button>
</body>
<script>
var loginLayer = (function(){
var div = document.createElement('div');
div.innerHtml = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild(div)
return div;
})();
document.getElementById('loginBtn').onclick = function() {
loginLayer.style.display = 'block'
}
</script>
</html>
当打开一个网站时,需要登录,但登录的弹窗只会在点击登录按钮的时候出现,甚至有的网站不需要登录就能直接浏览。那么很有可能将白白浪费一些DOM节点。这时我们并不需要再页面加载时就创建一个弹窗。 正确的打开方式可以用下面的方式实现。
登录通用的惰性单例的实现就是要抽离所有的单例模式都是要实现的----控制只有一个对象。那么我们来看看控制只有一个对象的操作抽象出来是个什么样子:
var obj;
if (!obj) {
obj = xxx
}
现在我们局把如何管理单例的逻辑从原来的代码抽离出来,这些逻辑被封装在getSingle函数内部,创建对象的方法fn被当成参数动态传入getSingle函数:
var getSingle = function(fn) {
var result;
return function(){
return result || (result = fn.apply(this, arguments))
}
}
接下来将用于创建登录浮窗的方法用参数fn的形式传入getSingle,我们可以不仅传入createLoginLayer,还能传入createScript、createIframe等。之后再让getSingle返回一个新的函数,并且用一个变量result来保存fn的计算结果。result变量因为保存在闭包中,它通用的惰性单例永远不会被销毁。在将来的请求中,如果resutlt已经被赋值,那么它将返回这个值。代码如下。
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'
}
至此我们实现了一个getSingle函数来帮我们实现只有一个实例对象的目的,并且将实例对象要做的职责独立出来,两个方法互不干扰。