全部 / 前端 / 技术 · 2022年5月16日 0

单例模式

默认标题_公众号封面首图_2022-05-14+17_30_05

定义:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

实现单例

如何实现单例?

无非是用一个变量来标识当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。

class Singleton {
  instance = null;
  static getInstance() {
    if (!this.instance) {
      this.instance = new Singleton()
    }
    return this.instance
  }
  prototypeMethods() {
    console.log('Mes')
  }
}
console.log(Singleton.getInstance() === Singleton.getInstance()) // true

这里的 instance 属性就是那个实例是否创建的标识。单例的核心代码如下:

let instance;
if(!instance){
    instance = xxx
}

应用:

  1. 登录框 :无论点击多少次,就只有一个登录弹框;
  2. Vuex:单一数据源;

登录框:

class LoginForm {
  instance = null;
  static showLogin() {
    if (this.instance) {
      return this.instance;
    }
    this.createForm();
    return (this.instance = this);
  }
  static createForm() {
    console.log("create");
    let form = document.createElement("div"),
      input = document.createElement("input");

    input.placeholder = '输入用户名'
    form.appendChild(input);
    document.body.appendChild(form);
  }
}

LoginForm.showLogin()

Vue 中单例组件:


扩展

如何把本来不是一个单例的函数如何改造为一个单例呢,难道要像上面添加标识和方法吗?如果都这样做的话有点麻烦,我们可以抽象一个单例工厂方法:


function SingletonFactory(fn) {
  if (!fn.instance) {
    fn.instance = new (Function.prototype.bind.apply(fn, arguments));
  }
  return fn.instance
}

案例:

class Fun {
  constructor(project, year) {
    this.project = project;
    this.year = year;
  }
  say() {
    console.log("Fun say", this.project);
  }
}
console.log(SingletonFactory(Fun, "前端黑板报", 2017) == SingletonFactory(Fun, "前端黑板报", 2017))

注:new 的时候传入参数,参考 https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible

或者:

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

使用:

function Test() {
  console.log(1)
  return this
}

let getSingleTest = getSingle(Test)

console.log(getSingleTest() == getSingleTest()) // 1 true

或者使用代理类:

let ProxySingleton = (function () {
  let instance
  return function (fn) {
    if (!instance) {
      instance = new (Function.prototype.bind.apply(fn, arguments))();
    }
    return instance
  }
}())

使用:

function Test(...args) {
  //console.log(args)
}

console.log(new ProxySingleton(Test,1,2) === new ProxySingleton(Test,1,2)) // true