单例模式:用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。

实现单例模式

//1.实现单例模式
var Singleton = function (name) {
  this.name = name;
};
Singleton.prototype.getName = function() {
  alert(this.name);
};
Singleton.getInstance = (function() {
  var instance = null;
  return function(name) {
    if (!instance) {
      instance = new Singleton(name);
    }
    return instance;
  }
})();
var a = Singleton.getInstance('sven1');
var b = Singleton.getInstance('sven2');

console.log(a === b); //true

以上,我们通过 Singleton.getInstance 来获取 Singleton 类的唯一对象,虽然简单但是增加了这个类的“不透明性”,Singleton 类的使用者必须知道这是一个单例类,跟以往通过 new xxx 的方法不同

透明的单例模式

有了以上的不足,我们现在就来解决 φ(* ̄0 ̄)>

//透明的单例模式 
var CreateDiv = (function() {
  var instance;
  var CreateDiv = function(html) {
    if (instance) {
      return instance;
    }
    this.html = html;
    this.init();
    return instance = this;
  };
  
  CreateDiv.prototype.init = function() {
    var div = document.createElement('div');
    div.innerHTML = this.html;
    document.body.appendChild(div);
  };
  
  return CreateDiv;
})();
var aa = new CreateDiv('sven1');
var bb = new CreateDiv('sven2');

console.log(aa === bb);

以上代码,CreateDiv 做了创建对象和执行初始化 init 方法,且保证只有一个对象。 但是,假如有一天我们需要利用这个类,在页面中创建千千万万个 div,既要让这个类从单例类变成一个普通的可以产生多个实例的类,那我们必须改写 CreateDiv 构造函数,把控制创建唯一的那一段去掉…这样好像有点太麻烦了

用代理实现单例模式

这里就可以引入代理类的方式,思想就是将负责管理的单例逻辑移到一个代理类中,这样 CreateDiv 就变成一个普通的类,代码此处省略…

JavaScript中的单例模式

前面几种单例模式的实现,更多是接近传统面向对象语言中的实现,单例从“类”中创建出来。 然而,js 作为一个没有类的语言,生搬单例模式的概念并无意义。我们只要一个对象,为什么要创建一个类呢?我们只是要一个唯一的实例就好了..因此我们可以直接用 var a = {} 来创建对象字面量。

惰性单例

惰性单例:需要的时候才创建。 例如,以一个登录悬浮窗为例,我们可以在页面加载的时候创建好这个 div 窗口,然后点击登录按钮的时候开始显示,但是这样如果用户只是随便浏览下就白白浪费了一些 dom 节点。 还有一种方法,点击的时候再创建,关闭的时候销毁。但是如果用户频繁的进行注销等操作,我们就要频繁的创建和删除div,显然这也不合理。 所以,这时候惰性单例模式就出马了。

var createLoginLayer = (function() {
	var div;
	return function() {
		if (!div) {
			div = document.createElement('div');
			//创建添加等dom操作...
		}
		return div;
	}
})();

$("xxx").onclick = function() {
	var loginLayer = createLoginLayer();
	loginLayer.show();
}

通用的惰性实例

看起来好像没什么问题,然而,他还是违反了单一的原则,创建的对象逻辑都放在createLoginLayer内部,如果我们要创建别的东西例如iframe,就要几乎将这个函数照抄一遍了,因此我们应该把不变的部分隔离出来。

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

var createLoginLayer = function() {
	//创建登录框
}

var createSingleLoginLayer = getSingle(createLoginLayer);

于是,目的达成。

当然,单例模式用途远远不止于创建对象,比如我们在渲染完页面的时候,接下来要给这个列表绑定 click 事件,如果是 ajax 动态向列表里面加载数据,实际上只需要第一次渲染的时候绑定。如果不用 jq$("").one('click', function(){}) 的话,我们也可以用单例模式,做法类似。