JS设计模式-单例模式

2019-08-15 admin

单例模式

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

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

实现单例模式

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

在构造器里记录实例:

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

参考

曾探. JavaScript设计模式与开发实践 (图灵原创) (Chinese Edition)

[转载]原文链接:https://segmentfault.com/a/1190000020085308

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-72601.html

文章标题:JS设计模式-单例模式

相关文章
从2014年的发展来展望JS的未来将会如何
&lt;font face=&quot;寰�杞�闆呴粦, Arial, sans-serif &quot;&gt;2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
jsdom 中文文档(纯翻译)
jsdom是一个纯粹由 javascript 实现的一系列 web标准,特别是 WHATWG 组织制定的DOM和 HTML 标准,用于在 nodejs 中使用。大体上来说,该项目的目标是模拟足够的Web浏览器子集,以便用于测试和挖掘真实世界...
2018-05-14
three.js实现围绕某物体旋转
话不多说,请看代码: 可以拖动右上角观察变化 &lt;!DOCTYPE html&gt; &lt;html lang=&quot;en&quot; style=&quot;width: 100%; height:100%;&quot;&gt...
2017-02-17
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
NodeJS参考手册pdf版
下载地址:Nodejs参考手册PDF版下载 ...
2015-11-12
JS中的语音合成——Speech Synthesis API
JS中的语音合成——Speech Synthesis API 简介 HTML5中和Web Speech相关的API实际上有两类,一类是“语音识别(Speech Recognition)”,另外一个就是“语音合成(Speech Synthes...
2018-05-17
Node.js学习(1)----HTTP服务器与客户端
Node.js 标准库提供了 http 模块,其中封装了一个高效的 HTTP 服务器和一个简易的HTTP 客户端。http.Server 是一个基于事件的 HTTP 服务器,它的核心由 Node.js 下层 C++部分实现,而接口由 Jav...
2015-11-12
使用jspdf生成pdf报表
由于前台html已经动态生成报表,而且,前台有一个功能,一个date range组件,当你拖动的时候,报表会在不提交到后台的情况下动态变化。 因此需要用到js生成生报表: 用到的组件: jquery.js jspdf.js canvg.js...
2017-03-25
Riot.js:不足1KB的MVP客户端框架
Riot.js是一款MVP(模型-视图-呈现)开源客户端框架,其最大的特点就是体积非常小,不足1KB,虽然体积小,但它可以帮助用户构建大规模的Web应用程序。 Riot.js是由Moot公司开发,目前最新版本为v0.9.2,遵循MIT开源许...
2016-03-11
javaScript+turn.js实现图书翻页效果实例代码
为了实现图书翻页的效果我们在网上可以看到很多教程 在这里推荐turn.js 网上的turn.js 有api 不过是英文的  很多人看起来不方便 .关于代码也是奇形怪状在这里我将详细讲解如何使用turn.js实现翻页效果 ,本篇文章只是讲解 ...
2017-03-16
回到顶部