js 从一段单例的代码说起

158 阅读2分钟

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

从一段仿java的代码说起

首先我们参照java 写一段js的单例

 var SingleTon function(name){
   this.name = name;
   this.inctance = null;
 }

SingleTon.prototype.getName = function(){
  console.log(this.name)
}

SingleTon.prototype.getInstance = function(name) {
  if(!this.instance) {
    this.instance = new SingleTon(name);
  }
  
  return this.instance;
}


var a = SingleTon.getInstance('a');
var b = SingleTon.getInstance('b');


console.log(a===b)

虽然这是一个最简单的单例, 但是很明显, 这个类的不透明性与正统的js写法是相悖的。 在js中, 获取一个类的写法是 new XXX, 这里要 SingleTon.getInstance 来获取, 这种生搬java的模式,对于js而言, 并没有太大的意义。

结合业务场景,使用代理实现单例模式

 var createDiv = function(name){
   this.html = html;
   this.init();
 }
 
 instance.ptototype.init = function(){
   var div = document.createElement('div');
   div.innerHtml = this.html;
   document.body.appendChild(div);
 }
 
 var proxySingle = (function(){
   var instance;
   return function(html){
     if(!this.instance){
       instance = new createDiv();
     }
     return instance;
   }
 })()
 
 var a = new proxySingle('a');
 var a = new proxySingle('b');
 

将管理单例模式, 移动到了代理中, createDiv和proxySingle组合实现了单例模式。

正统的js写法实现单例模式

由于js是一门无类的语言,以上生搬硬套的写法,能否归属到奇技淫巧之列, 长此以往, 必然会走上邪路。

js 创建对象的方法很简单, 单例的核心只是确保只有一个实例, 并且全局可以访问, 我们为何一定要创建一个类呢?

 var a = {};

a实际上就是一个单例, 但是减少全局变量污染, 我们可以

  1. 使用命名空间
 const nameSpace = {
   a: function(){
     console.log('a')
   },
   b:function(){
     console.log('b')
   }
 }
  1. 使用闭包封装变量
  const user = (function(){
     var __name = 'zehui',
     __age = 29;
     return {
      getUserInfo(){
        return `${__name}{__age}`
      }
     }
  })()

总结

因为语言的差异性, 相比于传统的实现单例的模式, js有更适合的方法。 但是最然简单, 但是实用性大大超乎我们的想象, 单例在 Vue 的源码中, 也有大量的运用。而将创建对象和管理单例的职责划分开, 使用组合的方式来实现。