文章目录

  • 1、实现单例模式
  • 2、透明的单例模式
  • 3、用代理实现单例模式
  • 4、JavaScript 中的单例模式
  • 5、惰性单例
  • 6、通用的惰性单例
  • 7、小结

定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏
览器中的 window 对象等

1、实现单例模式

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

var Singleton = function (name) {this.name = name;this.instance = null;};Singleton.getInstance = function (name) {if (!this.instance) {this.instance = new Singleton(name);}return this.instance;};var a = Singleton.getInstance('a');var b = Singleton.getInstance('b');console.log(a === b); // true

或者利用闭包:

var Singleton = function (name) {this.name = name;this.instance = null;};Singleton.getInstance = (function (name) {var instance = null;return function (name) {if (!instance) {instance = new Singleton(name);}return instance;};})();var a = Singleton.getInstance('a');var b = Singleton.getInstance('b');console.log(a === b); // true

问题:
就是增加了这个类的“不透明性”,Singleton 类的使用者必须知道这是一个单例类

2、透明的单例模式

实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样。

在下面的例子中,我们将使用 CreateDiv 单例类,它的作用是负责在页面中创建唯一的 div 节点,代码如下:

var CreateDiv = (function () {var instance = null;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('a');var b = new CreateDiv('b');console.log(a === b);

问题:
问题:假设我们某天需要利用这个类,在页面中创建千千万万的 div,即要让这个类从单例类变成一个普通的可产生多个实例的类,那我们必须得改写 CreateDiv 构造函数,把控制创建唯一对象的那一段去掉,这种修改会给我们带来不必要的烦恼

解决:
使用代理模式

3、用代理实现单例模式

在 CreateDiv 构造函数中,把负责管理单例的代码移除出去,使它成为一个普通的创建 div 的类:

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('a');var b = new ProxySingletonCreateDiv('b');console.log(a === b);

跟之前不同的是,现在我们把负责管理单例的逻辑移到了代理类 proxySingletonCreateDiv 中。这样一来,CreateDiv 就变成了一个普通的类,它跟 proxySingletonCreateDiv 组合起来可以达到单例模式的效果

4、JavaScript 中的单例模式

在 JavaScript 开发中,我们经常会把全局变量当成单例来使用。
例如:

var a = {};

问题:
全局变量存在很多问题,它很容易造成命名空间污染

解决:
1、使用命名空间

var namespace1 = {a: function(){alert (1);},b: function(){alert (2);} };

2、使用闭包封装私有变量

var user = (function(){var __name = 'sven',__age = 29;return {getUserInfo: function(){return __name + '-' + __age;}} })();

5、惰性单例

惰性单例指的是在需要的时候才创建对象实例
例子:

var createLoginLayer = (function () {var div;return function () {if (!div) {div = document.createElement('div');div.innerHTML = '我是浮窗';document.body.appendChild(div);}return div;}})()// 用户点击按钮的时候才开始创建该浮窗document.getElementById('loginBtn').onclick = function () {var loginLayer = createLoginLayer();};

问题:违反单一职责原则,创建对象和管理单例的逻辑都放在 createLoginLayer对象内部,如果我们下次需要创建页面中唯一的 iframe,或者 script 标签,用来跨域请求数据,就
必须得如法炮制,把 createLoginLayer 函数几乎照抄一遍
解决:第6点

6、通用的惰性单例

将单例的逻辑从原来的代码中抽离出来,这些逻辑被封装在 getSingle函数内部,创建对象的方法 fn 被当成参数动态传入 getSingle 函数

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

使用示例:

var createLoginLayer = function () {var div = document.createElement('div');div.innerHTML = '我是浮窗';document.body.appendChild(div);return div;}var createSingleLoginLayer = getSingle(createLoginLayer);document.getElementById('loginBtn').onclick = function () {var loginLayer = createSingleLoginLayer();};

也可以用在只执行一次的函数
比如:给一个div绑定事件

var bindEvent = getSingle(function () {console.log('给div绑定事件~');return true;});bindEvent();bindEvent();bindEvent();

7、小结

单例模式是一种简单但非常实用的模式,特别是惰性单例技术,在合适的时候才创建对象,并且只创建唯一的一个。更奇妙的是,创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。