Javascript的设计模式之单例模式

428 阅读3分钟

单例模式

定义

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

一个最简单的单例模式

最简单的单例模式非常容易实现,只需要一个变量去储存new出来的对象,下次再去这个对象时,只需要判断变量里是否有之前创建好的对象,如果有就直接返回,没有再重新new一个对象。

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;
};
var b = Singleton.getInstance("sven2");
var a = Singleton.getInstance("sven1");
alert(a === b); // true

JavaScript中的单例模式

前面提到的一种单例模式的实现,更多的是接近传统面向对象语言中的实现,单例对象从“类”中创建而来。

JavaScript是一个无类的语言。所以不需要生搬单例模式的概念。对象直接用 const a = {} 便能创建 单例模式的核心就是确保只有一个实例,并提供全局访问。

因此,实际上在很多情况下,我们可以直接用全局变量来当做单例模式的实现。 但是,全局变量有很多问题,比如造成命名空间污染。可以用以下方法去解决。

  1. 使用命名空间。 命名空间不能杜绝全局变量,只能一定程度上减少全局变量。
  2. 使用闭包封装私有变量 只暴露一些接口,去得到变量。
var user = (function () {
  var __name = "sven",
      __age = 29;
  return {
    getUserInfo: function () {
      return __name + "-" + __age;
    },
  };
})();

一个需求中的单例模式实际应用

var createLoginLayer = (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("loginBtn").onclick = function () {
  var loginLayer = createLoginLayer();
  loginLayer.style.display = "block";
};

这这块代码中,在点击loginBtn时会触发函数,创建一个弹窗层的对象,而且用闭包存储了这个节点,下次调用会直接返回之前创建好的节点,无疑是运用了单例模式。

但是这么做,是违反了单一职责原则的,创建和管理单例的逻辑都放在了createLoginLayer这个对象内部。而后果也显而易见,如果你想要创建一个页面中唯一的iframe或者是script去请求跨域数据,都需要把这个函数抄一遍。

因此我们完全可以抽离出来一个单例模式的逻辑,用一个变量来标志是否创建过对象,如果是,则在下次直接返回这个已经创建好的对象

let obj;
if ( !obj ){
    obj = x;
}

将逻辑抽离出来放在getSingle里,而参数是一个函数,就是上文中的创建登录浮窗的方法。

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

这样一来我们只需要关注自己的方法如何实现,只要使用getSingle去包裹一下实现的方法,生成的函数,就已经实现了单例模式。

var createLoginLayer = function () {
  var div = document.createElement("div");
  div.innerHTML = "我是登录浮窗";
  div.style.display = "none";
  document.body.appendChild(div);
  return div;
};
var createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById("loginBtn").onclick = function () {
  var loginLayer = createSingleLoginLayer();
  loginLayer.style.display = "block";
};

总结

在实现一个可以运用在项目中的单例模式,需要对闭包以及高阶函数有一定了解,可以最大程度的去解耦,创建对象或者说实现一个方法是我们需要关注的内容,而如何管理单例,是一个通用的方法,完全可以将其抽离出来,两个一结合就是一个完美的单例模式。