Skip to content

单例模式(Singleton)

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

解析:即实现单例要满足两点。一、仅且有一个实例;二、有一个访问它的全局访问点。

使用场景

  • 线程池
  • 全局缓存
  • 登录框
  • 事件只绑定一次 等等

一般我们想到的实现方法是:

javascript
const CreateDiv = (function () {
  let instance;
  const CreateDiv = function (html) {
    if (instance) return instance; // 重点在于这步,判断是否有实例化一次
    this.html = html;
    this.init();
    return instance = this;
  }

  CreateDiv.prototype.init = function () {
    const div = document.createElement('div');
    div.innerText = this.html;
    document.body.appendChild(div);
  }

  return CreateDiv;
})();

const a = new CreateDiv('kim1');
const b = new CreateDiv('kim2');
console.log(a === b) // true

缺点:这个构造函数怪怪的。而且当我们需要实现一个 iframe 或者其他需求的时候,又要重新复制一次代码进行改写。

  1. 使用变量来判断
javascript
const createLoginLayer = (function () {
  let div;
  return function () {
    if (!div) {
      div = document.createElement('div');
      div.innerHTML = '我是浮窗';
      div.style.display = 'none';
      document.body.appendChild(div);
    }
    return div;
  }
})();

const popup = createLoginLayer();

缺点:违反了单一职责原则,创建对象和管理单例的逻辑都放在一个函数里,如果下一次业务不同,则又需要照抄一遍。

最终方案:将管理单例和创建对象分开开了,以后则需要传入不同的创建对象函数则可以实现单例模式。

javascript
function f(fn) {
  let instance;
  return function () {
    return instance || (instance = fn.apply(this, arguments))
  }
}

function a() {
  const temp = new Object();
  return temp;
}

const create = f(a)
console.log(create() === create())  // true

例如:实现一个登陆弹窗,当第一次点击登陆时,创建弹窗的对象,第二次则继续沿用之前创建好的对象。 PS:这里省略样式和弹窗的文本,只是简单的添加一个元素到 body 节点上。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<button id="login">登陆</button>

<script>
  document.getElementById('login').addEventListener('click', function () {
    const loginDiv = login()
    console.log(loginDiv)
  })

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

  function createDiv() {
    const div = document.createElement('div');
    div.innerHTML = '我是浮窗';
    // TODO add style or edit HTML
    document.body.appendChild(div);
    return div
  }

  const login = getSingle(createDiv);
</script>
</body>
</html>

Released under the MIT License.