实现单例模式

  1. /** 最简单的单例实现
  2. * 缺点:Singleton类的使用者必须知道这是一个单例类,跟以往通过new XXX的方式来获取对象不同,这里偏要使用Singleton.getInstance来获取对象
  3. * 所以此种方式创建单例的意义并不大
  4. */
  5. var Singleton = function(name) {
  6. this.name = name;
  7. this.instance = null;
  8. };
  9. Singleton.prototype.getName = function() {
  10. alert(this.name);
  11. };
  12. Singleton.getInstance = function(name) {
  13. if (!this.instance) {
  14. this.instance = new Singleton(name);
  15. }
  16. return this.instance;
  17. };
  18. var a = Singleton.getInstance('sven1');
  19. var b = Singleton.getInstance('sven2');
  20. alert(a === b); // true

透明的单例模式

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

  1. var CreateDiv = ((function () {
  2. var instance = null;
  3. var CreateDiv = function (html) {
  4. if (instance) return instance;
  5. this.html = html;
  6. this.init();
  7. return instance = this;
  8. };
  9. return CreateDiv;
  10. })())
  11. CreateDiv.prototype.init = function () {
  12. var div = document.createElement('div');
  13. div.innerHTML = this.html;
  14. document.body.appendChild(div);
  15. }
  16. alert(a === b); // true

CreateDiv的构造函数实际上负责了两件事情。第一是创建对象和执行初始化init方法,第二是保证只有一个对象。虽然我们目前还没有接触过“单一职责原则”的概念,但可以明确的是,这是一种不好的做法,至少这个构造函数看起来很奇怪。
假设我们某天需要利用这个类,在页面中创建千千万万的div,即要让这个类从单例类变成一个普通的可产生多个实例的类,那我们必须得改写CreateDiv构造函数,把控制创建唯一对象的那一段去掉,这种修改会给我们带来不必要的烦恼。

用代理实现单例模式

  1. var CreateDiv = function (html) {
  2. this.html = html;
  3. this.init();
  4. }
  5. CreateDiv.prototype.init = function () {
  6. var div = document.createElement('div');
  7. div.innerHTML = this.html;
  8. document.body.appendChild(div);
  9. }
  10. var ProxySingletonCreateDiv = (function () {
  11. var instance = null;
  12. return function (html) {
  13. return instance || (instance = new CreateDiv(html));
  14. }
  15. })();
  16. var a = new ProxySingletonCreateDiv('AAA');
  17. var b = new ProxySingletonCreateDiv('BBB');
  18. alert(a === b); // true

JavaScript中的单例模式

作为普通的开发者,我们有必要尽量减少全局变量的使用,即使需要,也要把它的污染降到最低。以下几种方式可以相对降低全局变量带来的命名污染.

  1. 使用命名空间
  1. var namespace1 = {
  2. a: function () {
  3. alert(1);
  4. },
  5. b: function () {
  6. alert(2);
  7. },
  8. }
  1. 使用闭包封装私有变量
  1. var user = (function () {
  2. var __name = 'sven',
  3. __age = 12;
  4. return {
  5. getUserInfo: function () {
  6. return __name + '-' + __age;
  7. }
  8. }
  9. })();

惰性单例

  1. var createLoginLayer = (function() {
  2. var div;
  3. return function() {
  4. if (!div) {
  5. div = document.createElement('div');
  6. div.innerHTML = ’我是登录浮窗’;
  7. div.style.display = 'none';
  8. document.body.appendChild(div);
  9. }
  10. return div;
  11. }
  12. })();
  13. document.getElementById('loginBtn').onclick = function() {
  14. var loginLayer = createLoginLayer();
  15. loginLayer.style.display = 'block';
  16. };

上面这段代码还有以下的问题:

  1. 这段代码仍然是违反单一职责原则的,创建对象和管理单例的逻辑都放在createLoginLayer对象内部。
  2. 如果我们下次需要创建页面中唯一的iframe,或者script标签,用来跨域请求数据,就必须得如法炮制,把createLoginLayer函数几乎照抄一遍

通用的惰性单例

  1. var getSingle = function (fn) {
  2. var result = null;
  3. return function () {
  4. return result || (result = fn.apply(this, arguments));
  5. }
  6. }
  7. var createLoginLayer = function (...args) {
  8. console.warn(args); // [1, 2, 3]
  9. var div = document.createElement('div');
  10. div.innerHTML = '我是登录浮窗';
  11. div.style.display = 'none';
  12. document.body.appendChild(div);
  13. return div;
  14. };
  15. var singletonCreateLoginLayer = getSingle(createLoginLayer);
  16. document.querySelector('#demo').addEventListener('click', function () {
  17. var layer = singletonCreateLoginLayer(1,2,3);
  18. layer.style.display = 'block';
  19. });