Skip to content

定义:

简明的定义:第一个对外有且仅有一个实例【只提供一个实例】,这种编码方案就是单件设计模式

应用场景:

  • 1、vuex、React-Redux全局状态管理容器store对象
  • 2、localStorage:相同协议、主机名、端口下只有一份localStorage
  • 3、日志文件类(日志只有一个对象)

构建单件设计模式

  • 1、构造器设置为私有,不允许外部创建类的实例
  • 2、至少应该提供一个外部访问的方法或属性,通过它得到对象,所以这个方法应该是静态方法
  • 3、外部调用静态方法获取一个对象

举个例子:

typescript
class MyLocalStorage {
  
  static localStorage: MyLocalStorage
  private constructor() {

  }

  public static getInstance() {
    if(!this.localStorage) {
      this.localStorage = new MyLocalStorage()
    }
    return this.localStorage
  }

  public setItem(key: string, value: any) {
    localStorage.setItem(key, JSON.stringify(value))
  }

  public getItem(key: string) {
    const value = localStorage.getItem(key)
    return value ? JSON.parse(value) : null
  }
}

静态方法相当于es5: 构造函数.静态方法

对象方法相当于es5: 构造函数.prototype.对象方法

实现一个Storage,使得该对象为单例,基于localStorage进行封装
实现方法setItem(key, value)和getItem(key)

javascript
// 静态方法版
class Storage {
  static getInstance() {
    // 判断是否已经new过1个实例
    if(!Storage.instance) {
      // 若这个唯一的实例不存在,那么先创建它
      Storage.instance = new Storage()
    }
    // 如果这个唯一的实例已经存在,则直接返回
    return Storage.instance
  }

  getItem(key) {
    return localStorage.getItem(key)
  }
  setItem(key, value) {
    return localStorage.setItem(key, value)
  }
}


/**
 * 测试代码
 * 
 * const storage1 = Storage.getInstance()
 * const storage2 = Storage.getInstance()
 * 
 * storage1.setItem('name', 'zack')
 * storage1.getItem('name')  // zack
 * 
 * storage2.getItem('name') // zack
 * 
 * storage1 === storage2  // true
 * 
 * 
 * 
 */
javascript
// 闭包版

// 先实现一个基础的StorageBase类,把getItem和setItem方法放在它的原型链上
function StorageBase() {}
StorageBase.prototype.getItem = function(key) {
  return localStorage.getItem(key)
}
StorageBase.prototype.setItem = function(key, value) {
  return localStorage.setItem(key, value)
}

// 以闭包的形式创建一个引用自由变量的构造函数
const Storage = (function() {
  let instance = null
  return function() {
    // 判断自由变量是否为null
    if(!instance) {
      // 如果为null则new出唯一实例
      instance = new StorageBase()
    }
    return instance
  }
})()

/**
 * 测试代码
 * // 这里不用new Storage的形式调用,直接Storage()也会有一样的效果
 * const storage1 = new Storage()
 * const storage2 = new Storage()
 * 
 * storage1.setItem('name', 'zack')
 * 
 * storage1.getItem('name')   // zack
 * 
 * storage2.getItem('name')  // zack
 * 
 * storage1 === storage2  // true
 * 
 * 
 */

实现一个全局的模态框(全局唯一的Modal弹框)

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>单例模式弹框</title>
</head>
<style>
  #modal {
    height: 200px;
    width: 200px;
    line-height: 200px;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    border: 1px solid black;
    text-align: center;
  }
</style>
<body>
  <button id="open">打开弹窗</button>
  <button id="close">关闭弹框</button>
</body>
<script>
  // 核心逻辑,这里采用了闭包思路来实现单例模式
  const Modal = (function() {
    let modal = null
    return function() {
      if(!modal) {
        modal = document.createElement('div')
        modal.innerHTML = '我是一个全局唯一的Modal'
        modal.id = 'modal'
        modal.style.display = 'none'
        document.body.appendChild(modal)
      }
      return modal
    }
  })()

  // 点击打开按钮展示模态框
  document.getElementById('open').addEventListener('click', function() {
    // 未点击则不创建modal实例,避免不必要的内存占用
    // 此处不用new Modal的形式调用也可以
    const modal = new Modal()
    modal.style.display = 'block'
  })

  // 点击关闭按钮隐藏模态框
  document.getElementById('close').addEventListener('click', function() {
    const modal = new Modal()
    if(modal) {
      modal.style.display = 'none'
    }
  })
</script>
</html>