Javascript单例模式

一、什么是单例模式

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

单例模式的实现原理是:用一个变量来标识当前类是否已经创建了实例,如果创建了就直接返回该实例,如果没有创建就创建新的实例。

二、实现单例模式

1.简单的单例模式

let Singleton = function (name) {
  this.name = name
}

Singleton.prototype.say = function () {
  console.log(this.name)
}

Singleton.getInstance = (function () {
  let instance = null
  return function (name) {
    if (!instance) {
      return new Singleton(name)
    }
    return instance
  }
})()

let s1 = Singleton.getInstance('jack')
let s2 = Singleton.getInstance('rose')

console.log(s1 === s1) // true

这里我们通过Singleton.getInstance来获取Singleton的唯一对象,但是这种获取对象的方式有些别扭,与传统的new Singleton有些区别,这样就增加了这个类的“不透明性”,即使用必须知道这是一个单例模式,需要通过Singleton.getInstance来获取Singleton的唯一对象。

2.透明的单例模式

let CreateDiv = (function (params) {
  let instance
  let CreateDiv = function (html) {
    if (instance) {
      return instance
    }
    this.html = html
    this.init()
    return instance = this
  }
  CreateDiv.prototype.init = function () {
    let div = document.createElement('div')
    div.innerHTML = this.html
    document.body.appendChild(div)
  }
  return CreateDiv
})()
let a = new CreateDiv('a')
let b = new CreateDiv('b')
console.log(a==b) // true

这样我们就创建了一个透明的单例模式,但是构造函数CreatedDiv看起来有些奇怪,它调用了init方法并保证了只创建一个对象,所以这样不符合单一职责原则。

所以我们引入代理类:

let CreateDiv = function (html) {
  this.html = html
  this.init()
}
CreateDiv.prototype.init = function () {
  let div = document.createElement('div')
  div.innerHTML = this.html
  document.body.appendChild(div)
}
let ProxySingle = (function () {
  let instance
  return function (html) {
    if (!instance) {
      instance = new CreateDiv(html)
    }
    return instance
  }
})()
let a = ProxySingle('a')
let b = ProxySingle('b')
console.log(a===b) // true

这样我们把负责管理单例的逻辑移到了代理类ProxySingle上,这样CreateDiv类就只负责创建元素,变成了一个普通的类。

3、JavaScript的单例模式

javascript是一门没有类的语言,所以在我们创建一个唯一的对象的时候,不需要像传统的语言一样先声明一个类。

javascript中虽然全局变量不是单例模式,但是在实际开发中我们经常会把全局变量当作单例模式来使用。因为当我们在全局变量下创建一个对象确实是独一无二的,并且该全局变量提供给全局访问,满足了确保只有一个实例,并提供全局访问的核心思想。

但是全局变量确实也存在诸多的问题,比如在大型项目中全局变量过多往往会造成命名空间的污染。所以我们应该避免使用过多的全局变量,我们可以使用命名空间闭包的来解决全局变量污染的问题。

3.1、惰性单例

惰性单例是指在需要的时候才创建对象实例。比如我们前面的instance实例总是在调用Singleton.getInstance才被创建。

let createLoginLayer = (function () {
    let div
    return function () {
        if(!div) {
            div = document.createElement('div')
        }
        return div
    }
})()
document.getElementById('btn').onclick = function () {
    let loginLayer = createLoginLayer()
    loginLayer.style.display = 'block'
}

3.1、通用的惰性单例

上面的例子任然违反了单一职责模式,因为创建对象和管理对象单例逻辑的代码都放在createLoginLayer,而却代码的复用性不强,如果我们需要创建的不是一个div而是其他东西就需要把一样的代码重新写一遍。所以我们需要把创建对象和管理单例逻辑的代码分开

首先我们创建一个管理单例的函数:

let getSingle = function (fn) {
    let res
    return function () {
        return res || (res = fn.apply(this, arguments))
    }
}

接下来我们将创建对象的函数作为参数传入getSingle中,之后我们返回一个函数,并且用一个变量resfn的返回结果保存起来。因为res在自身的变量中,所以永远不会被销毁。在之后的请求中如果res已经被赋值则直接返回该值。

使用:

// 创建DIV
let createLoginLayer = function () {
    let div = document.createElement('div')
    return div
}
let createSingleLoginLayer = getSingle(createLoginLayer)
document.getElementById('btn').onclick = function () {
    let loginLayer = createLoginLayer()
    loginLayer.style.display = 'block'
}
// 创建script
let createScript = function () {
    let script = document.createElement('script')
    return script
}
let createSingleScript = getSingle(createScript)
document.getElementById('btn1').onclick = function () {
    let scriptDom = createSingleScript()
    doument.body.appenChild(scriptDom)
}

总结

单例模式是一种简单但非常实用的模式,特别是惰性单例,只在需要的时候才创建对像,并且只创建唯一的一个。更奇妙的是,创建对象和管理单例逻辑的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力,提高的代码的复用性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值