单例模式的定义是:保证一个类仅有一个实例,并提供访问它的全局访问点
实现单例模式
要实现一个标准的单例模式并不复杂,无非是一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。
var Singleton = function (name) {
this.name = name
this.instance = null
}
Singleton.prototype.getName = function () {
console.log(this.name)
}
Singleton.getInstance = function (name) {
if (!this.instance) {
this.instance = new Singleton(name)
}
return this.instance
}
var a = Singleton.getInstance('sven1')
var b = Singleton.getInstance('sven1')
console.log(a === b) // true
or
var Singleton = function (name) {
this.name = name
}
Singleton.prototype.getName = function () {
console.log(this.name)
}
Singleton.getInstance = (function () {
var instance = null
return function (name) {
if (!instance) {
instance = new Singleton(name)
}
return instance
}
})()
透明的单例模式
我们现在的目标是实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使 用其他任何普通类一样。在下面的例子中,我们将使用 CreateDiv 单例类,它的作用是负责在页 面中创建唯一的 div 节点,代码如下:
var CreateDiv = (function () {
var instance
var CreateDiv = function (html) {
if (instance) {
return instance
}
this.html = html
this.init()
return (instance = this)
}
CreateDiv.prototype.init = function () {
var div = document.createElement('div')
div.innerHTML = this.html
document.body.appendChild(div)
}
return CreateDiv
})()
var a = new CreateDiv('sven1')
var b = new CreateDiv('sven2')
console.log(a === b)
用代理实现单例模式
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 ProxySingletonCreteDiv('sven1')
var b = new ProxySingletonCreteDiv('sven1')
console.log(a === b)
通过引入代理类的方式,我们同样完成了一个单例模式的编写,跟之前不同的是,现在我们 把负责管理单例的逻辑移到了代理类 proxySingletonCreateDiv 中。这样一来,CreateDiv 就变成了 一个普通的类,它跟 proxySingletonCreateDiv 组合起来可以达到单例模式的效果。
JavaScript中的单利模式
全局变量不是单例模式,但在 JavaScript 开发中,我们经常会把全局变量当成单例来使用。
使用命名中间
把 a 和 b 都定义为 namespace1 的属性,这样可以减少变量和全局作用域打交道的机会
var namespace1 = {
a: function () {
console.log(1)
},
b: function () {
console.log(2)
},
}
我们还可以动态地创建命名空间 ```javascript var MyApp = {}
MyApp.namespace = function (name) { var parts = name.split(‘.’) var current = MyApp for (var i in parts) { if (!current[parts[i]]) { current[parts[i]] = {} } current = current[parts[i]] } }
MyApp.namespace(‘event’) MyApp.namespace(‘dom.style’)
console.dir(MyApp)
// 上诉代码等价于
var MyApp = { event: {}, dom: { style: {}, }, }
<a name="QJRjq"></a>
#### 使用闭包封装私有变量
```javascript
var user = (function () {
var __name = 'sven'
var __age = 29
return {
getUserInfo: function () {
return __name + '-' + __age
},
}
})()
惰性单例模式
惰性单例指的是在需要的时候才会创建对象实例
Singleton.getInstance = (function () {
var instance = null
return function (name) {
if (!instance) {
instance = new Singleton(name)
}
return instance
}
})()
登录浮窗的单例实现
我们登录网页qq的时候,需要登录,此时的登录应当只有一个
- 当我们点击的时候,才会弹出来(不点击的时候,压根就不创建)
```javascript
var createLoginLayer = (function () {
var div
return function () {
if (!div) {
} return div } })()div = document.createElement('div')
div.innerHTML = '我是登录浮窗'
div.style.display = 'none'
document.body.appendChild(div)
document.getElementById(‘loginBtn’).onclick = function () { var loginLayer = createLoginLayer() loginLayer.style.display = ‘block’ }
<a name="j6RoB"></a>
#### 通用的惰性单例
```javascript
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 = createSingleLoginLayer()
loginLayer.style.display = 'block'
}