js设计模式之单例模式

好久好久没有写博客了。写博客是个很好的习惯,现在重新开始吧。

国内谈js的设计模式的书也就那么几本,找了本javascript设计模式与开发实战(曾探)开始学习。9102年要努力起来。进入正文吧。以下代码全部使用es6实现。

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

核心就是确保只有一个实例,并提供全局访问。

因为有些内容我们只需要一个,比如线程池,全局缓存,window对象。在页面中,比如登录弹窗,无论点击多少次登录按钮,页面中只有一个登录框,那么它就是唯一的,适合用单例模式创建。以下单例都是惰性单例,就是说在使用的时候才会创建它, 这样是很合理的。

1.实现单例模式

class SingleTon {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }

  static getInstance(name) {
    if (!this.instance) {
      SingleTon.instance = new SingleTon(name);
    }

    return SingleTon.instance;
  }
}

SingleTon.instance = null;

const a = SingleTon.getInstance('小鸡');
const b = SingleTon.getInstance('小鸟');

console.log(a === b); // true
复制代码

调用的时候通过SingleTon.getInstance来获取单例对象,这种方式编写方式相对简单,但是也增加了这个类使用的复杂度,原因在于开发者必须知道要通过SingleTon.getInstance来创建单例对象,而不是更为熟悉的new SingleTon()来创建。接下来改造一下。

2.透明的单例模式

这里要通过new xxx()来构建单例,这种方式更为熟悉。假设页面中需要一个div(可以假想成登录框),并且只需要这一个。

const CreateDiv = (() => {
  let instance = null;

  class CreateDiv {
    constructor(html) {
      if(!instance) {
        instance = this;
        this.html = html;
        this.init();
      }
      return instance;
    }

    init() {
      const div = document.createElement('div');
      div.innerHTML = this.html;
      document.body.appendChild(div);
    }
  }

  return CreateDiv;
})();

const a = new CreateDiv('登录框内容小鸡');
const b = new CreateDiv('登录框内容小鸟');

console.log(a === b); // true
复制代码

这种方式相当于原始的SingleTon.getInstance有了改善,但是它仍然存在问题。 这段代码中class CreateDiv实际上负责了两件事情。第一是创建对象并且保证只存在一个对象,第二是执行初始化方法。这让人感觉很不好,它违背了单一职责原则

假如某天需要利用这个类,在页面中创造div都由它负责,那么要改动这个类的代码,把控制创建唯一对象的这一段去掉,这种修改虽然不是很麻烦,但是它是可以避免的。所以我们可以将上述的两件事情分开,分别用两段代码实现,保证单一职责原则。

3.用代理实现单例模式

class CreateDiv {
  constructor(html) {
    this.html = html;
    this.init();
  }

  init() {
    const div = document.createElement('div');
    div.innerHTML = this.html;
    document.body.appendChild(div);
  }
}

const proxySingleTonCreateDiv = (() => {
  let instance = null;
  return html => {
    if (!instance) {
      instance = new CreateDiv(html);
    }
    return instance;
  };
})();

// 这里的new需不需要都可以,为了称作代理类,还是加上了
const a = new proxySingleTonCreateDiv('小鸡'); 
const b = new proxySingleTonCreateDiv('小鸟'); 

console.log(a === b); // true
复制代码

在这里,CreateDiv只负责创造div,proxySingleTonCreateDiv是个代理类。它们组合起来就可以达到单例模式的效果,并且不同的类职责单一了。就算想创建多个div也可以达到了。

这种方式已经足够完美,不够还可以继续改进。那就是proxySingleTonCreateDiv只能代理CreateDiv这个类,如果后来想要代理CreateIframe、CreateScript它可就无能为力了,所以接下来的改造就是要达到代理任何类的目的。

4.通用的惰性单例

/*
var getInstance = function(fn) {
  var result = null;
  return function() {
    return result || (result = fn.apply(this, arguments));
  };
};
*/

class GetInstance {
  constructor(fn) {
    let result = null;
    return () => {
      return result || (result = fn.apply(this, arguments));
    };
  }
}

var createDiv = new GetInstance(function() {
  var div = document.createElement('div');
  document.body.appendChild(div);
  return div;
});

var createIframe = new GetInstance(function() {
  var iframe = document.createElement('iframe');
  document.body.appendChild(iframe);
  return iframe;
});

const div = createDiv();
const div2 = createDiv();
div.innerHTML = '小鸡';
div2.innerHTML = '小鸟';
console.log(div === div2); // true

const iframe = createIframe();
const iframe2 = createIframe();
console.log(iframe === iframe2); // true

复制代码

如上,GetInstance没必要使用class的,只是为了使用es6的写法。可以使用注释部分代码,就不需要使用new了。

这种写法是通用的写法,GetInstance中传入你想要构造的内容,GetInstance保证它是单例,很类似react的高阶组件。然后createXXX()返回创建的对象,你还可以自定义对象的内容,比如上述的div和div2就是了,它们是同一个对象。也可以在GetInstance传入的方法中去给创建的div去赋默认值。

转载于:https://juejin.im/post/5c2b5f38f265da61407f0179

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值