JS设计模式:单例模式

更新日期: 2019-07-15阅读: 1.8k标签: 模式

单例模式

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。 ———来自维基百科 

一个很典型的应用是在点击登录按钮,弹出登录浮窗,不论点击多少次登录按钮,页面始终只会弹出一个登录浮窗。


实现单例模式

思路很简单,用一个变量记录是否已经为某个类创建过对象,如果没有则返回新建的对象,反之则返回之前创建的对象。


在构造器里记录实例:

var Singleton = function(name) {
  this.name = name;
  this.instance = null;
};

Singleton.prototype.getName = function() {
  console.log(this.name);
};

Singleton.getInstance = function(name) {
  if (!this.instance) {
    this.instance = new Singleton(name);
  }
  return this.instance;
};

var a = Singleton.getInstance('sean1');
var b = Singleton.getInstance('sean2');
console.log(a === b); // true


使用闭包记录实例:

var Singleton = function(name) {
  this.name = name;
};

Singleton.prototype.getName = function() {
  console.log(this.name);
};

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

以上方法相对简单,但是Singleton类的使用者必须知道这是一个单例类,需要调用getInstance()函数(而不是new的方式)来获取对象,这也就是增加了“不透明性”。


透明的单例模式

利用一个IIFE形成一个闭包,在里边通过变量instance来记录实例,并返回构造函数。

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('sean1');
var b = new CreateDiv('sean2');
console.log(a === b); // true

上面完成了一个透明的单例类的编写,但还是有缺点,增加了一些程序的复杂度,且阅读性差。还有重要的一点是违反了“单一职责原则”,接下来再改进一下。


用代理实现单例模式

其实就是将实际的业务代码与负责管理单例的代码分离,管理单例的类就是代理类。

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);
};

var ProxySingletonCreateDiv = (function() {
  var instance;
  return function(html) {
    if (!instance) {
      instance = new CreateDiv(html);
    }
    return instance;
  };
})();

var a1 = new ProxySingletonCreateDiv('sean1');
var b1 = new ProxySingletonCreateDiv('sean2');
console.log(a1 === b1); // true

// 如果想要创建多个 div 就直接调用 CreateDiv 咯

var a2 = new CreateDiv('sean1');
var b2 = new CreateDiv('sean2');
console.log(a2 === b2); // false


JavaScript 中的单例模式

前面提到的几种实现,更多的是接近传统面向对象语言中的实现,但 JavaScript 是一门无类(class-free)语言,我们只要记住单例模式但核心是 确保只有一个实例,并提供全局访问

全局变量不是单例模式,但它却满足单例的条件,所以我们经常会把全局变量当成单例来使用:var a = {};。但是我们都知道全局变量很容易造成命名空间污染、容易被不小心覆盖等问题。我们有必要尽量减少全局变量的使用,即使要,也要将污染降到最低:

使用命名空间

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

// 或者动态创建命名空间:

var MyApp = {};
MyApp.namespace = function(name) {
  var parts = name.split('.');
  var current = MyApp;
  for (var item of parts) {
    if (!current[item]) {
      current[item] = {};
    }
    current = current[item];
  }
};
MyApp.namespace('event');
MyApp.namespace('dom.style');

console.dir(MyApp);

使用闭包封装私有变量

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


惰性单例

所谓惰性,就是只有在需要的时候才会去做。前面提到的也有满足惰性的实现,不过是基于“类”的。下面来看一下文章开头提到的那个典型的应用实现:

<html>
  <body>
    <button id="loginBtn">登录</button>
    <script>
      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';
      };
    </script>
  </body>
</html>

上面的代码完成了惰性单例的实现,但仍然违反了单一职责原则,如果我们下次不是创建一个登录浮窗而是别的元素呢,请往后看通用的惰性单例。


通用的惰性单例

// 定义一个管理单例的函数:
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 = '我是登录浮窗';
  div.style.display = 'none';
  document.body.appendChild(div);
  return div;
}

// 获取单例的浮窗
var createSingleLoginLayer = getSingle(createLoginLayer);

document.getElementById('btn').onclick = function() {
  var loginLayer = createSingleLoginLayer();
  loginLayer.style.display = 'block';
};

链接: https://fly63.com/article/detial/4742

js设计模式之单例模式,javascript如何将一个对象设计成单例

单例模式是我们开发中一个非常典型的设计模式,js单例模式要保证全局只生成唯一实例,提供一个单一的访问入口,单例的对象不同于静态类,我们可以延迟单例对象的初始化,通常这种情况发生在我们需要等待加载创建单例的依赖。

前端设计模式:从js原始模式开始,去理解Js工厂模式和构造函数模式

工厂模式下的对象我们不能识别它的类型,由于typeof返回的都是object类型,不知道它是那个对象的实例。另外每次造人时都要创建一个独立的person的对象,会造成代码臃肿的情况。

JavaScript设计模式_js实现建造者模式

建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象

html和xhtml,DOCTYPE和DTD,标准模式和兼容模式

主要涉及知识点: HTML与XHTML,HTML与XHTML的区别,DOCTYPE与DTD的概念,DTD的分类以及DOCTYPE的声明方式,标准模式(Standard Mode)和兼容模式(Quircks Mode),标准模式(Standard Mode)和兼容模式(Quircks Mode)的区别

前端四种设计模式_JS常见的4种模式

JavaScript中常见的四种设计模式:工厂模式、单例模式、沙箱模式、发布者订阅模式

javascript 策略模式_理解js中的策略模式

javascript 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句,策略模式提供了开放-封闭原则,使代码更容易理解和扩展, 策略模式中的代码可以复用。

javascript观察者模式_深入理解js中的观察者模式

javascript观察者模式又叫发布订阅模式,观察者模式的好处:js观察者模式支持简单的广播通信,自动通知所有已经订阅过的对象。存在一种动态关联,增加了灵活性。目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。

Vue中如何使用方法、计算属性或观察者

熟悉 Vue 的都知道 方法methods、计算属性computed、观察者watcher 在 Vue 中有着非常重要的作用,有些时候我们实现一个功能的时候可以使用它们中任何一个都是可以的

我最喜欢的 JavaScript 设计模式

我觉得聊一下我爱用的 JavaScript 设计模式应该很有意思。我是一步一步才定下来的,经过一段时间从各种来源吸收和适应直到达到一个能提供我所需的灵活性的模式。让我给你看看概览,然后再来看它是怎么形成的

Flutter 设计模式 - 简单工厂

在围绕设计模式的话题中,工厂这个词频繁出现,从 简单工厂 模式到 工厂方法 模式,再到 抽象工厂 模式。工厂名称含义是制造产品的工业场所,应用在面向对象中,顺理成章地成为了比较典型的创建型模式

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!