单例模式

53 阅读2分钟

单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。有些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等。

1. 实现单例模式

主要思路:用一个变量来标志当前是否已经为某个类创建过对象,如果是,那么在下次获取该类的实例时,直接返回之前创建的对象。

版本一:使用类的静态属性和静态方法实现单例模式

const Singleton = function (name) {
  this.name = name
  this.instance = null
}
// 原型链上挂载的方法
Singleton.prototype.getName = function () {
  return this.name
}
// 静态方法
Singleton.getInstance = function (name) {
  if (!this.instance) {
    this.instance = new Singleton(name)
  }
  return this.instance
}
const a = Singleton.getInstance('sven1')
const b = Singleton.getInstance('sven2')
console.log(a === b)

版本二:使用闭包和静态方法实现单例模式

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

const a = Singleton.getInstance('aaa')
const b = Singleton.getInstance('bbb')
console.log(a === b)

版本三:惰性单例

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

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <button id="loginBtn">登录</button>
  <script>
    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'
    }
  </script>
</body>
</html>

2. 实践中的单例模式

2.1 axios取消重复请求

import axios from "axios";
const CancelToken = axios.CancelToken;
let cancelId = 0;
let cancelArray = [];

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在请求时,可以添加自己的特点标识去筛选出需要重复取消的接口
  const source = CancelToken.source();
  cancelId++;
  const id = cancelId;
  config.cancelId = id;
  config.cancelToken = source.token;
  const cancelIndex = cancelArray.findIndex(e => e.url === config.url);
  cancelArray.push({
    id,
    url: config.url,
    source
  })
  if (cancelIndex > -1) {
    cancelArray[cancelIndex].source.cancel('取消重复请求');
    cancelArray.splice(cancelIndex, 1)
  }
  return config;
}, function (error) {
  return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  const cancelIndex = cancelArray.findIndex(e => e.id === response.cancelId);
  if (cancelIndex >= -1) {
    cancelArray.splice(cancelIndex, 1)
  }

  // 对响应数据做点什么
  return response;
}, function (error) {
  if (axios.isCancel(error)) {
    // 如果是取消的接口,可以自行返回一个特定标识
    console.log('isCancel')
  } else {
    // 对响应错误做点什么
    return Promise.reject(error);
  }
});

export default axios

参考链接:

  1. JavaScript设计模式与开发实践
  2. JavaScript设计模式之单例模式