设计模式 - 单例模式

传统单例实现

基础实现

// 方式一:instance维护在对象中
// ES5
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 a = Singleton.getInstance('sven1')
var b = Singleton.getInstance('sven2')
alert(a === b) // true

// ES6 && TS
class Demo {
  private static instance: Demo // 仅当前类内部&仅类内部 访问
  static getInstance() { // 仅类内部 访问
    if(!this.instance) {
      this.instance = new Demo()
    }
    return this.instance
  }
}

// 缺点:
// 1. 构造函数中,使用了与该构造函数无关的变量标识 instance
// 2. 增加了这个类的“不透明性”。(使用者必须知道这是一个单例类,跟以往通过 new XXX 的方式来获取对象不同,这里要使用 Singleton.getInstance 来获取对象)

// 方式二:instance维护在闭包中
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')
alert(a === b) // true

// 缺点:
// 1. 增加了这个类的“不透明性”。

透明的单例模式

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 a = new CreateDiv('sven1');
var b = new CreateDiv('sven2');
alert(a === b); // true

// 缺点:
// 1. 违反“单一职责原则”(构造函数做了2件事:1.创建对象和执行初始化 init 方法 2.保证只有一个对象)
// 2. 不好维护。如果单例类变成一个普通的可产生多个实例的类,必须得改写 CreateDiv 构造函数,增加了出错的风险

用代理实现单例模式

var CreateDiv = function (html) {
  this.html = html;
  this.init();
};
CreateDiv.prototype.init = function () {
  var div = document.createElement('div');
  div.innerHTML = this.html;
  document.body.appendChild(div);
};

// 来引入代理类 proxySingletonCreateDiv
var ProxySingletonCreateDiv = (function () {
  var instance;
  return function (html) {
    if (!instance) {
      instance = new CreateDiv(html);
    }
    return instance;
  }
})();

var a = new ProxySingletonCreateDiv('sven1');
var b = new ProxySingletonCreateDiv('sven2');
alert(a === b);

// 优点
// 1.符合“单一职责原则” (负责管理单例的逻辑移到了代理类 proxySingletonCreateDiv 中。这样一来,CreateDiv 就变成了一个普通的类)

针对JavaScript常用的单例模式

JavaScript 其实是一门无类(class-free)语言,也正因为如此,生搬单例模式的概念并无
意义。在 JavaScript 中创建对象的方法非常简单,既然我们只需要一个“唯一”的对象,为什
么要为它先创建一个“类”呢?这无异于穿棉衣洗澡,传统的单例模式实现在 JavaScript 中并
不适用。

惰性单例

惰性单例指的是在需要的时候才创建对象实例。而不是在页面加载好
的时候就创建。这样如果交互场景下,没有被使用到,更节省资源。

// 惰性单例 - 创建过登录浮窗
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';
};

通用的惰性单例

将创建单例和功能函数拆开

// 通用惰性单例函数
var getSingle = function (fn) {
  var result;
  return function () {
    return result || (result = fn.apply(this, arguments));
  }
};

// 使用1 创建登录弹窗
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';
};

// 使用2 创建 iframe
var createIframe = function () {
  var iframe = document.createElement('iframe');
  document.body.appendChild(iframe);
  return iframe;
};
var createSingleIframe = getSingle(createIframe);
document.getElementById('loginBtn').onclick = function () {
  var loginLayer = createSingleIframe();
  loginLayer.src = 'http://baidu.com';
};

参考资料

[1] 曾探. JavaScript设计模式与开发实践. 北京:人民邮电出版社,2015

posted @ 2023-01-13 17:20  Better-HTQ  阅读(21)  评论(0编辑  收藏  举报