单例模式的定义:保证一个类仅有一个实例,并提供一个访问他的全局访问点。
透明的单例模式
我们现在的目标是实现一个透明的单例类,用户丛这个类中创建对象的时候,可以像使用其他任何普通类一样。在下面的例子中,我们将使用CreateDiv单例类,他的作用是负责在页面中创建唯一个的div节点,代码如下:
var CreateDiv = (function () {
var instance;
var CreateDiv = function (html) {
if (instance){
return instance
}
this.html = html;
this.init()
}
CreateDiv.prototype.init = function () {
var div = document.createElement('div')
div.innerHTML = this.html;
document.body.appendChild(div)
}
return CreateDiv
})()
var a = new CreateDiv('div1')
var b = new CreateDiv('div2')
console.log( a=== b) // true
虽然现在完成了一个透明的单例类的编写,但他同样有一些缺点。在这段代码中,CreateDiv构造函数扶着了两件事情,1.创建对象和执行初始化方法init,2.保证只有一个对象。
假设我们某天需要利用这个类,在页面冲创建很多个div,即要让这个类从单例类变成一个普通可以产生多个实例的类,那就需要改写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)
}
var ProxySingletonCreateDiv = (function () {
var instance;
return function (html) {
if (!instance) {
instance = new CreateDiv(html)
}
return instance;
}
})()
var a = new ProxySingletonCreateDiv('a')
var b = new ProxySingletonCreateDiv('b')
console.log( a === b)
javascript中的单例模式
// 1.使用命名空间
var namespace = {
a: function () {
console.log(a)
},
b: function () {
console.log(b)
}
}
// 2.使用闭包封装私有变量
// 这种方法把一些变量封装在闭包的内部,只暴露一些接口和外界通信
var user = (function () {
var _name = 'lusen', _age = 26;
return{
getName: function () {
return _name
},
getAge: function () {
return _age
}
}
})()
// 我们用下划线来约定私有变量,他们被封装在闭包产生的作用域中,
//外部是访问不到这两个变量的,这就避免了对全局的污染
惰性单例
惰性单例指的是在需要的时候去创建对象实例。
我们现在实现一个点击按钮弹出对话框的demo,代码如下:
var createDialog = function () {
var div = document.createElement('div')
div.innerHTML = '我是弹框'
div.style.display = 'none'
document.body.appendChild(div)
return div
}
document.getElementById('btn').onclick = function () {
var div1 = createDialog()
div1.style.display = 'block'
}
虽然现在达到了惰性的目的,但失去了单例的效果。当我们每次点击登录按钮的时候,都会创建一个新的div,这时候我们可以用一个变量来判断是否创建过div了,代码如下:
var createDialog = (function () {
var div;
return function (){
if (!div) {
var div = document.createElement('div')
div.innerHTML = '我是弹框'
div.style.display = 'none'
document.body.appendChild(div)
}
return div
}
})()
document.getElementById('btn').onclick = function () {
var div1 = createDialog()
div1.style.display = 'block'
}
通用的惰性单例
上面我们完成了一个可用的惰性单例,但是还有以下问题
- 微单单一职责原则,创建对象和管理单例的逻辑都放在了createDialog内部
- 如下下次我们需要创建页面中的一个iframe,就需要重复写一遍代码,这时候需要把管理单例的逻辑在代码里面抽离出来,这些逻辑封装在getSIngle内部,创建对象的fn方法被当做参数传入到getSingle函数中:
var getSingle = function (fn) {
var result;
return function () {
return result || (result = fn.apply(this, arguments))
}
}
var createDialog = function () {
var div = document.createElement('div')
div.innerHTML = '我是弹框'
div.style.display = 'none'
document.body.appendChild(div)
return div;
}
var createSingleDialog = getSingle(createDialog)
document.getElementById('btn').onclick = function () {
var divDialog = createSingleDialog()
divDialog.style.display = 'block'
}
在这个例子中,我们把创建实例对象的职责和管理单例的职责分别放置在两个方法里,这两个方法可以独立变化,互不影响,当他们联系在一起的时候,就完成了创建唯一实例对象的功能。
网友评论