[读书笔记] JavaScript设计模式: 单例模式

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

一种简单、方便的写法就是用一个变量来标识当前类是否已经创建过对象,如果有,则返回已经创建好的对象,否则创建一个新对象,并将其返回。

var Singleton = function(name) {
  this.name = name;
  this.instance = null;
}


Singleton.prototype.getName = function() {
  alert(this.name);
}

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

但是上述写法有一个问题,那就是使用Singleton这个类的时候,必须参考API文档,否则使用者不知道必须通过getInstance()方法而不是通过new的方式来获取对象,这就是增加了这个类的“不透明”性。

修改上述实现方式,使其变得“透明”些。

var Singleton = (function() {
  var instance = null;

  var Singleton = function(name) {
    if (!instance) {
      this.name = name;
      instance = this;
    }

    return instance;
  }

  Singleton.prototype.getName = function() {
    alert(this.name)  
  }

  return Singleton;
})()

修改后的Singleton类使用起来和其他的类没有区别,只要new Singleton('name')就可以了,但是上述写法也有一点小问题,那就是在Singleton的构造函数中做了两件事,一是创建对象并初始化name等变量,二是保证了该类只会存在一个对象,违反了设计原则中的“单一职责原则”的概念。如果后续要将Singleton改为非单例类,就必须要修改Singleton的构造函数。

为了使职责分的更加清楚,我们引入代理的方式来实现单例。

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

Manager.prototype.getName = function() {
  alert(this.name);
}

var Singleton = (function() {
  var instance;
  return function(name) {
    if (!instance) {
      instance = new Manager(name)
    }
    return instance
  }
})()

通过引入代理类,保证了各个类之间的职责单一。

上述是通过类的方式来创建单例对象,在其他语言中经常会看到这样的写法,如Java,C#等。但是JavaScript其实是一门无类(class-free)的语言,想要一个对象,直接创建就可以了,没有必要先创建类,再通过类创建对象,这样做无异于穿棉衣洗澡。所以说基于“类”的单例在JavaScript中其实并不适用。

在实际项目中经常会用到摸态框,一般来说,一个页面的模态框是唯一的,最常见的实现方式有以下两种方式:

  1. 在页面加载完成的时候便创建好模态框,当然这个模态框是隐藏的
  2. 当真正需要显示模态框时再创建

第一种方式简单,但是可能会浪费DOM元素,因为部分用户可能一直用不到这个模态框。第二种方式就需要用到将要学习的惰性单例。

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

var createModal = function() {
  var div = document.createElement('div');
  div.innerHTML = 'modal';
  document.body.appendChild(div);
  return div;
}

将getSingle方法提取出来,以后不光可以用于createModal,createXhr,createIframe等等都可以与getSingle组合使用。