javascript设计模式-单例模式

最近在重新修炼js的设计模式,发现平时自己所写的代码,无意中就使用到了某种的设计模式,所以特意记录一下,以便以后自己查看。
一.单例模式

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

单例模式,是一种很常见的模式,至少在我现在工作中经常用到。单例模式所强调的就是,有且仅有一个对象,并且这个对象是全局变量。那么,它的使用场景主要集中在登陆弹窗内容提示框loading加载组件等。

二.实现单例模式
想要实现一个标准的单例模式并不复杂,无非就是用一个变量标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。

		  var Singleton = function(name) {
				// 定义一个构造函数
				this.name = name;
				this.instance = null;
			};
			Singleton.prototype.getName = function() {
				// 原型上添加getName方法
				console.log(this.name);
			};
			Singleton.getInstance = function(name) {
				if (!this.instance) {
					// 如果不存在对象,则实例化Singleton
					this.instance = new Singleton(name);
				}
				return this.instance;
			};
			var a = Singleton.getInstance('kafei');
			var b = Singleton.getInstance('lvcha');
			console.log(a === b); // true 同一个对象

三.惰性单例
惰性单例指的是在需要的时候才创建对象实例。假设我们需要做一个唯一的弹窗。
第一种方案是在页面加载完成的时候便创建好这个div弹窗,这个弹窗一开始肯定是隐藏的状态,当用户点击登陆按钮的时候,它才会显示:

var loginLayer = (function () {
      var div = document.createElement("div");
      div.innerHTML = "登陆弹窗";
      div.style.display = "none";
      document.body.appendChild(div);
      return div;
    })()
    document.getElementById("btn").onclick = function () {
      loginLayer.style.display = "block";
    }

但是这种方式,存在问题,因为页面一旦加载,那么他就已经创建好了节点。如果我不希望它一开始就追加节点,而是用户点击按钮的时候,才创建这个节点。所以,需要改造一下代码:

var loginLayer = function () {
      var div = document.createElement("div");
      div.innerHTML = "登陆弹窗";
      div.style.display = "none";
      document.body.appendChild(div);
      return div;
    }
    document.getElementById("btn").onclick = function () {
      var loginLayer = loginLayer();
      loginLayer.style.display = "block";
    }

虽然现在已经达到了惰性的目的,但失去了单例的效果。但我们每次点击按钮的时候,都会新创建一个新的div,这显然是一个不合理的情况。
那么,我们可以使用一个变量来判断是否已经创建过了div节点。

var createLayer = (function () {
      var div;
      return function () {
        if (!div) {
          div = document.createElement("div");
          div.innerHTML = "登陆弹窗";
          div.style.display = "none";
          document.body.appendChild(div);
        }
        return div;
      }
    })()
    document.getElementById("btn").onclick = function () {
      var loginLayer = createLayer();
      loginLayer.style.display = "block";
    }

四.通用的惰性单例
虽然上面我们已经完成了一个惰性的单例模式,但还是会存在以下问题:
上面代码违反了单一职责原则,创建对象和管理单例的逻辑都在了createLayer 对象内容。
如果我们下次需要创建页面中唯一的iframe,则必须如法炮制,把createLayer函数再抄一遍,这显然是不合理的。

var createIframe = (function () {
      var iframe;
      return function () {
        if (!iframe) {
          iframe = document.createElement("iframe");
          iframe.style.display = "none";
          document.body.appendChild(iframe);
        }
        return iframe;
      }
    })()

现在我们需要不变的部分抽离出来,不去考虑它创建的是div还是一个iframe,创建唯一一个对象的逻辑是一致的,用一个对象来标志是否创建过对象,如果是则在下次返回这个创建好的对象:

var obj;
if (!obj) {
	obj = xxx;
}

那么就编写一个getSingle函数,这个函数接受一个函数(fn)作为参数,而这个fn这是创建对象的一个方法,然后再利用函数闭包中变量不会被销毁的特性,来判断这个对象是否存在。

var getSingel = function (fn) {
      var result;
      return function () {
        return result || (result = fn.apply(this, arguments)); // 保持this不变和能够接受参数
      }
    }
var loginLayer = function (ele, txt) {
      return function () {
        var oEle = document.createElement(ele);
        if (txt) {
          oEle.innerHTML = txt;
        }
        oEle.style.display = "none";
        document.body.appendChild(oEle);
        return oEle;
      }
    }
    var getSingel = function (fn) {
      var result;
      return function () {
        return result || (result = fn.apply(this, arguments)); // 保持this不变和能够接受参数
      }
    }
    var createSingleLayer = getSingel(loginLayer("div", "div"));
    // var createIfame = getSingel(loginLayer("iframe", "iframe"));
    document.getElementById("btn").onclick = function () {
      var layer = createSingleLayer();
      layer.style.display = "block";
    }

在这个例子中,我们把创建对象的职责和管理单例的职责放在两个不同的函数中,这两个函数相互独立而互不影响,当它们连接在一起的时候,就完成了创建唯一实例对象的功能。这个不得不感叹js的强大。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值