JavaScript设计模式之单例模式

242 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

定义

单例模式(Singleton Pattern):又被称为单体模式,是只允许实例化一次的对象类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,

好处

  • 在程序中多次使用同一个对象且作用相同时,为了防止频繁的创建对象和销毁使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

实现

单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。

对象字面量

全局对象就是最简单的单例模式。 如下:

var mySingleton = {
  property: 'xman',
  method: function () {
    console.log('hello');
  }
}

以上就是使用对象字面量的方法创建了单例对象mySingleton, 但是缺点也很明显:

  • 没有封装性,所有的属性方法都暴露出来;
  • 全局变量容易造成命名空间污染;
  • 对象一开始就创建,可能会用不上

下面我们来一步步的完善下。
第一步: 进行封装
我们需要添加一些私有属性和方法,然后通过闭包在内部封装这些变量和函数声明。 如下:

var mySingleton = function () {
  // 声明私有属性和方法
  var privateVariable = 'private variable';
  function privateMethod () {
    console.log(privateVariable);
  }
  // 返回公用变量和方法
  return {
    publicMethod: function () {
      privateMethod();
    },
    publicVariable: 'public variable'
  }
}

var singleton = Singleton();
singleton.publicMethod();  // 输出 'private variable'
console.log(singleton.publicVariable);  // 输出 'public variable'

ES6实现

利用ES6 中的class关键字和static关键字,其中static关键字是用来修静态方法的。

class Singleton {
  contructor (variable) {
    this.variable = variable;
    this.instance = null;
  }
  // 提供一个静态的方法来获取对象实例
  static getInstance (variable) {
    if (!this.instance) {
      this.instance = new Singleton(variable);
    }
    return this.instance;
  }
}

测试:

let instance1 = Singleton.getInstance('test');
let instance2 = Singleton.getInstance('test');

instance1 === instance2;   // true 是同一个实例

第二步: 延迟创建
就是在使用的时候才初始化,该如何处理?

var Singleton = (function() {
  // 单例实例引用
  var instance = null;
  function init() {
    return {
      publicMethod: function () {
        console.log('hello');
      },
      publicVariable: 'public variable'
    }
  }
  
  return {
    getInstance: function () {
      // 先判断,存在直接返回,如果不存在就创建
      if(!instance) {
        instance = init();
      }
      return instance;
    }
  }
})();

// 使用时调用公用的方法来获取实例
Singleton.getInstance().publicMethod();  // 输出 'hello'

代码中使用自执行匿名函数返回真正的单例类的构造方法 以上代码就是所谓延迟创建的形式,也被称为‘惰性创建’

通用的惰性单例

完善一个通用的惰性单例
可以将管理的逻辑从原来的代码中抽离出来,封装在getSingle函数内部,创建对象的方法fn被当做参数动态传入getSingle函数:

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

以下是创建唯一的iframe用于动态加载第三方页面:

var createSingleIframe = getSingle( function () {
  var iframe = document.createElement('iframe');
  document.body.appendChild( iframe );
  return iframe;
})

常见应用实例

登录框

点击登录按钮,只会弹出有且仅有一个登录框,即使后面再点击登录按钮,也不会再弹出多一个弹框。这就是单例模式的典型应用。

vuex中的store

  1. Vuex3.x版本 在Vuex3.x版本中是通过 Vue.use(Vuex) 安装Vuex插件,而Vuex 插件是一个对象,它在内部实现了一个 install 方法,这个方法会在插件安装时被调用,从而把 Store 注入到Vue实例里去。也就是说每 install 一次,都会尝试给 Vue 实例注入一个 Store

  2. VueX4版本
    vuex - createStore 代码如下:

export function createStore (options) {
  return new Store(options)
}

参考资料

# JavaScript设计模式与开发实践---曾探