【Javascript】设计模式之单例模式

文章目录

  • 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、小结

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

你可能感兴趣的:(#,设计模式,javascript,设计模式,单例模式)