单例模式的定义是:保证一个类仅有一个实例,并提供访问它的全局访问点

实现单例模式

要实现一个标准的单例模式并不复杂,无非是一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。

  1. var Singleton = function (name) {
  2. this.name = name
  3. this.instance = null
  4. }
  5. Singleton.prototype.getName = function () {
  6. console.log(this.name)
  7. }
  8. Singleton.getInstance = function (name) {
  9. if (!this.instance) {
  10. this.instance = new Singleton(name)
  11. }
  12. return this.instance
  13. }
  14. var a = Singleton.getInstance('sven1')
  15. var b = Singleton.getInstance('sven1')
  16. console.log(a === b) // true

or

  1. var Singleton = function (name) {
  2. this.name = name
  3. }
  4. Singleton.prototype.getName = function () {
  5. console.log(this.name)
  6. }
  7. Singleton.getInstance = (function () {
  8. var instance = null
  9. return function (name) {
  10. if (!instance) {
  11. instance = new Singleton(name)
  12. }
  13. return instance
  14. }
  15. })()

透明的单例模式

我们现在的目标是实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使 用其他任何普通类一样。在下面的例子中,我们将使用 CreateDiv 单例类,它的作用是负责在页 面中创建唯一的 div 节点,代码如下:

  1. var CreateDiv = (function () {
  2. var instance
  3. var CreateDiv = function (html) {
  4. if (instance) {
  5. return instance
  6. }
  7. this.html = html
  8. this.init()
  9. return (instance = this)
  10. }
  11. CreateDiv.prototype.init = function () {
  12. var div = document.createElement('div')
  13. div.innerHTML = this.html
  14. document.body.appendChild(div)
  15. }
  16. return CreateDiv
  17. })()
  18. var a = new CreateDiv('sven1')
  19. var b = new CreateDiv('sven2')
  20. console.log(a === b)

用代理实现单例模式

  1. var CreateDiv = function (html) {
  2. this.html = html
  3. this.init()
  4. }
  5. CreateDiv.prototype.init = function () {
  6. var div = document.createElement('div')
  7. div.innerHTML = this.html
  8. document.body.appendChild(div)
  9. }
  10. // 接下来引入代理类ProxySingletonCreateDiv
  11. var ProxySingletonCreateDiv = (function () {
  12. var instance
  13. return function (html) {
  14. if (!instance) {
  15. instance = new CreateDiv(html)
  16. }
  17. return instance
  18. }
  19. })()
  20. var a = new ProxySingletonCreteDiv('sven1')
  21. var b = new ProxySingletonCreteDiv('sven1')
  22. console.log(a === b)

通过引入代理类的方式,我们同样完成了一个单例模式的编写,跟之前不同的是,现在我们 把负责管理单例的逻辑移到了代理类 proxySingletonCreateDiv 中。这样一来,CreateDiv 就变成了 一个普通的类,它跟 proxySingletonCreateDiv 组合起来可以达到单例模式的效果。

JavaScript中的单利模式

  • 全局变量不是单例模式,但在 JavaScript 开发中,我们经常会把全局变量当成单例来使用。

    使用命名中间

    把 a 和 b 都定义为 namespace1 的属性,这样可以减少变量和全局作用域打交道的机会

    1. var namespace1 = {
    2. a: function () {
    3. console.log(1)
    4. },
    5. b: function () {
    6. console.log(2)
    7. },
    8. }
  • 我们还可以动态地创建命名空间 ```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: {}, }, }

  1. <a name="QJRjq"></a>
  2. #### 使用闭包封装私有变量
  3. ```javascript
  4. var user = (function () {
  5. var __name = 'sven'
  6. var __age = 29
  7. return {
  8. getUserInfo: function () {
  9. return __name + '-' + __age
  10. },
  11. }
  12. })()

惰性单例模式

  • 惰性单例指的是在需要的时候才会创建对象实例

    1. Singleton.getInstance = (function () {
    2. var instance = null
    3. return function (name) {
    4. if (!instance) {
    5. instance = new Singleton(name)
    6. }
    7. return instance
    8. }
    9. })()

    登录浮窗的单例实现

  • 我们登录网页qq的时候,需要登录,此时的登录应当只有一个

  • 当我们点击的时候,才会弹出来(不点击的时候,压根就不创建) ```javascript var createLoginLayer = (function () { var div return function () { if (!div) {
    1. div = document.createElement('div')
    2. div.innerHTML = '我是登录浮窗'
    3. div.style.display = 'none'
    4. document.body.appendChild(div)
    } return div } })()

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

  1. <a name="j6RoB"></a>
  2. #### 通用的惰性单例
  3. ```javascript
  4. var getSingle = function (fn) {
  5. var result
  6. return function () {
  7. return result || (result = fn.apply(this.arguments))
  8. }
  9. }
  1. var createLoginLayer = function () {
  2. var div = document.createElement('div')
  3. div.innerHTML = '我是登录浮窗'
  4. div.style.display = 'none'
  5. document.body.appendChild(div)
  6. return div
  7. }
  8. var createSingleLoginLayer = getSingle(createLoginLayer)
  9. document.getElementById('loginBtn').onclick = function () {
  10. var loginLayer = createSingleLoginLayer()
  11. loginLayer.style.display = 'block'
  12. }