javascript设计模式之单例模式

单例模式,是一种经常使用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。经过单例模式能够保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例。javascript

面向对象式的单例模式

单例模式的简单实现html

let Singleton = function(name) {
	this.name = name;
	this.instanse = null;
};
Singleton.prototype.getName = function() {
	console.log(this.name);
};
Singleton.prototype.getInstance = function(name) {
	if (!this.instanse) {
		this.instanse = new Singleton(name);
	}
	return this.instanse;
}
let instanse1 = Singleton.getInstance('instance');
let instanse2 = Singleton.getInstance('instance');
console.log(instanse1 === instanse2);  // true
let instanse3 = new Singleton('instance');
let instanse4 = new Singleton('instance');
console.log(instanse1 === instanse2);  // false
复制代码

这样虽然实现了单例模式的效果,可是有一个缺陷就是这种方式任然能够经过new Singleton(name)来建立新的对象。若是别的程序员不知道这是一个单例类,他可能会经过new指令来建立对象,而不是经过getInstance方法java

改进的方法程序员

let Singleton = (function () {
	let instance;
	let Singleton = function (name) {
		if (instance) {
			return instance;
		}
		this.name = name;
		return instance = this;
	};
	return Singleton;
})();
let a = new Singleton('name1');
let b = new Singleton('name2');
console.log(a.name);
console.log(b.name);
console.log(a===b);  // true
复制代码

借助匿名函数和闭包,实现对instance和Singleton构造函数的封装设计模式

下面使用这种方式来定义用来建立html中某个惟一元素,例如某个divbash

let SingleDiv = (function () {
	let div;
	let SingleDiv = function (html) {
		if (div) {
			return div;
		}
		this.html = html;
		this.init();
		return div = this;
	};
	SingleDiv.prototype.init = function() {
		let div = document.createElement('div');
		div.innerHTML = this.html;
		document.body.appendChild(div);
	}
	return SingleDiv;
})();
复制代码

html测试代码闭包

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>test</title>
</head>
<body>
  <script src="../test.js"></script>
  <script> let div1 = new SingleDiv('hello'); let div2 = new SingleDiv('world'); // 最终在屏幕呈现的是hello console.log(div1 === div2); // true </script>
</body>
</html>
复制代码

可是这种方法也有弊端,违反了单一职责原则,上面的设计方式使得SingleDiv完成了两样职责,建立div,div的初始化。这样的设计方式不利于修改和维护app

既然是不符合第一职责原则,那么咱们便把初始化的任务与建立任务分离函数

let CreateDiv = function(html) {
	this.html = html;
	this.init();
}
CreateDiv.prototype.init = function() {
	let div = document.createElement('div');
	div.innerHTML = this.html;
	document.body.appendChild(div);
}
复制代码
let SingleDiv = (function() {
	let div;
	return function(html) {
		if (!div) {
			div = new CreateDiv(html);
		}
		return div;
	}
})()
复制代码
let div1 = new SingleDiv('div1');
let div2 = new SingleDiv('div1');
console.log(a === b)
复制代码

javascript风格的单例模式

因为js实际上没有类的概念,因此js中实现单例模式很是简单。单例模式中只须要一个惟一的对象,并提供全局访问,因此咱们只须要在全局中建立一个须要的对象便可。虽然实现简单,可是同时形成了命名空间污染的问题。能够经过使用命名空间或使用闭包包装私有变量的方式解决命名空间污染的问题性能

饿汉式单例模式

假设须要实现点击某个按钮或连接会弹出一个登陆浮窗的效果,为了性能和维护的方便,须要将其设计成单例的。饿汉式单例模式就是无论是否须要,先建立须要的单例实例。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <button id="loginBtn">
    login
  </button>
  <script> let loginBox = (function() { let div = document.createElement("div"); div.innerText = "this is login window"; div.style.display = "none"; document.body.appendChild(div); return div; })(); document.getElementById("loginBtn").onclick = function() { loginBox.style.display = "block"; } </script>
</body>
</html>
复制代码

可是这种实现方式是有问题的,由于用户到这个页面也许不会进行登陆操做,采用饿汉式单例无论用户登陆仍是不登陆都会建立一个登陆浮窗div节点,并将其添加到html中,这形成了资源上的浪费。解决方法即是懒汉式单例模式

懒汉式单例模式

懒汉式单例模式,也被称为惰性单例。即只有在须要时才会建立。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <button id="loginBtn">
    login
  </button>
  <script> let createLoginBox = (function() { let div; return function() { // 当div不存在时才建立,保证明例惟一 if (!div) { div.innerText = "this is login window"; div.style.display = "none"; document.body.appendChild(div); } return div; } })(); document.getElementById("loginBtn").onclick = function() { let loginBox = createLoginBox(); // 第一次点击登陆时才建立 loginBox.style.display = "block"; } </script>
</body>
</html>
复制代码

如上解决方式依然有弊端:违反了单一职责原则;当须要建立惟一的其余元素时,例如iframe, script标签,就须要将上述代码再从新复制一份,只修改建立元素的名称,这增长了代码的冗余和维护成本。

解决方法:将建立对象和保证对象惟一的行为分离

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <button id="loginBtn">
    login
  </button>
  <script>
    let getSingleObject = function(fn) {
        let result;
        return function() {
            return result || (result = fn.apply(this, arguments))
        }
    }
    function createDiv() {
      let div = document.createElement("div");
      div.innerText = "this is login window";
      div.style.display = "none";
      document.body.appendChild(div);
      return div;
    }
    let createLoginBox = getSingleObject(createDiv);
    document.getElementById("loginBtn").onclick = function() {
      let loginBox = createLoginBox();
      loginBox.style.display = "block";
    }
  </script>
</body>
</html>
复制代码

经过上面的方式能够建立任意一个单例实例

相关文章
相关标签/搜索