JavaScript设计模式系列二:单例模式

单例模式

什么是单例模式?

单例模式的定义:一个类仅有一个实例,而且能够在全局访问
何时须要用到单例模式呢?其实单例模式在平常开发中的使用很是的普遍,例如各类浮窗、像登陆浮窗等,不管咱们点击多少次,都是同一个浮窗,浮窗从始至终只建立了一次。这种场景就十分适合运用单例模式。闭包


代码实现

咱们建立一个“最老的人”的类,很明显,“最老的人”有且只有一个。这很符合咱们单例模式的运用场景。咱们先来看看完整代码:函数

var oldestMan = function (name) {
  this.name = name;
}

oldestMan.prototype.getName = function () {
  console.log(this.name);
}
//引入一个代理函数和闭包的概念
var createOldestMan = (function () {
  var instance;
  return function (name) {
      if (!instance) {
         instance = new oldestMan(name);
      }
      return instance;
  }
})();

var personA = createOldestMan("holz");
var personB = createOldestMan("Amy");
personA.getName();  //  holz
personB.getName();  //  holz

咱们能够在控制台上看到即便调用了两次createOldestMan而且赋了不同的值,但两次getName()输出的都是第一次的“holz”。这就是单例模式。this

代码看不太懂?不要紧,如今给你们一一讲解。
首先咱们建立了一个oldestMan类,建立了一个name属性。而后咱们经过 prototype 给它添加一个getName()方法用来获取oldestMan的名字,相信到这里你们都是懂的,而后下面一段代码就是重点了,也比较难理解。咱们打这段代码单独拿出来将一下。prototype

//引入一个代理函数和闭包的概念
var createOldestMan = (function () {
  var instance;
  return function (name) {
      if (!instance) {
         instance = new oldestMan(name);
      }
      return instance;
  }
})();

首先,咱们不用管什么是代理函数,之因此叫它代理函数是由于它辅助咱们实现单例模式的效果,这段函数第一个关键点是 createOldestMan() 是一个当即执行函数。当即函数在声明的时候就会当即执行,也就是在声明createOldestMan的时候这个函数就会执行,它会声明一个instance 变量,而后返回一个函数给createOldestMan。createOldestMan就至关于:代理

var createOldestMan = function (name) {
      if (!instance) {
         instance = new oldestMan(name);
      }
      return instance;
  }

第二个关键点是:这里利用了 闭包 的概念。code

闭包是什么呢?我只须要记住当函数在定义时的语法做用域以外被调用,却还能访问定义时的语法做用域时,就是产生了闭包。

咱们来看一下咱们的代码,函数先定义了一个instance,而后再返回一个function(name),这个function(name)里面用到了instance变量。在正常状况下,在当即执行函数执行以后,instance变量就会被JavaScript的垃圾回收机制回收,可是由于function(name)被返回到了外部,而function(name)随时会被调用,随时会访问到instance变量,因此instance变量被保留在了内存中。这就产生了闭包。也就是说,function(name)被赋值给了外部的createOldestMan,在外部的语法做用域中执行,但还能够访问到定义时内部的语法做用域中的instance。ip

因此在 当即执行函数闭包 的做用下,instance只被申请了一次,也就是只有一个instance。也就是说,咱们不管执行多少次createOldestMan("..."),instance只会是第一次的那个值。因此咱们就能够判断instance是否已经被实例化了,来给instance赋值,若是instance已经被实例化,就返回instance。这就达到了一个类只有一个实例的效果。内存

通用的单例模式

我还能够改造一下代码,由于在开发中,咱们可能不只只有一个单例,因此咱们应该让代码可以变得各个单例通用。咱们应该在哪里改呢?没错,改代理函数。咱们只须要把代理函数中的oldestMan()提取出来,改成以参数的形式传值,不局限于oldestMan()。作用域

var singleObj;
var createSingleton = function (fn) {
  return function (text) {
    if (!singleObj) {
      singleObj = new fn (text);
    }
    return singleObj;
  }
}

这样咱们就能够把单例做为参数传进去,用它实现不一样的单例了。
完整代码是这样的:开发

var oldestMan = function (name) {
  this.name = name;
}

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

//一个通用的代理函数
var singleObj;
var createSingleton = function (fn) {
  return function (text) {
    if (!singleObj) {
    singleObj = new fn (text);
    }
    return singleObj;
  }
}

var person_1 = createSingleton(oldestMan)("holz");
var person_2 = createSingleton(oldestMan)("tom");
person_1.getName(); //holz
person_2.getName(); //holz

一样,即便再次调用createSingleton并传入不一样的值,输出的依旧是第一次的“holz”。


总结

单例模式的定义:一个类仅有一个实例,而且能够在全局访问。适用场景:其实单例模式在平常开发中的使用很是的普遍,例如各类浮窗、像登陆浮窗等,不管咱们点击多少次,都是同一个浮窗,浮窗从始至终只建立了一次。这种场景就十分适合运用单例模式。

相关文章
相关标签/搜索