JavaScript编程的单例设计模讲解


Posted in Javascript onNovember 10, 2015

在Javascript中,单例模式是一种最基本又经常用到的设计模式,可能在不经意间就用到了单例模式。
本文将从最基础的理论开始,讲述单例模式的基本概念和实现,最后用一个例子来讲述单例模式的应用。

理论基础

概念

单例模式,顾名思义就是只有一个实例存在。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

基本结构

最简单的单例模式起始就是一个对象字面量,它将有关联的属性和方法组织到一起。

var singleton = {
  prop:"value",
  method:function(){
  }
}

这种形式的单例模式,所有成员都是公开的,都可以通过singleton来访问。这样的缺点是单例中有一些辅助的方法并不希望暴露给使用者,如果使用者用了这些方法,然后在后面维护的时候,一些辅助方法被删除,这样会造成程序错误。
如何避免这样从的错误呢?

包含私有成员的单例模式

要怎么在类中创建私有成员呢,这通过需要闭包来进行实现,关于闭包的知识,本文不再赘述,大家可以自行Google。
基本形式如下:

var singleton = (function () {
      var privateVar = "private";
      return {
        prop: "value",
        method: function () {
          console.log(privateVar);
        }
      }
    })();

首先是一个自执行的匿名函数,在匿名函数中,声明了一个变量privateVar,返回一个对象赋值给单例对象singleton。在匿名函数外部无法访问到privateVar变量,它就是单例对象的私有变量,只能在函数内部或通过暴露出来的方法去访问这个私有变量。这种形式又被成为模块模式。

惰性实例化

不管是直接字面量或者私有成员的单例模式,两者都是在脚本加载时就被创建出来的单例,但是有时候,页面可能永远也用不到这个单例对象,这样会造成资源浪费。对于这种情况,最佳的处理方式就是惰性加载,就是说在需要的时候才去真正实例化这个单例对象,如何实现呢?

var singleton = (function () {
      function init() {
        var privateVar = "private";
        return {
          prop: "value",
          method: function () {
            console.log(privateVar);
          }
        }
      }
      var instance = null;
      return {
        getInstance: function () {
          if (!instance) {
            instance = init();
          }
          return instance;
        }
      }
    })();

首先将创建单例对象的代码封装到init函数中,然后声明一个私有变量instance表示单例对象的实例,公开一个方法getInstance来获取单例对象。
调用的时候就通过singleton.getInstance()来进行调用,单例对象是在调用getInstance的时候才真正被创建。

适用场合

单例模式是JS中最常使用的设计模式,从增强模块性和代码组织性等方面来说,应该尽可能的使用单例模式。它可以把相关代码组织到一起便于维护,对于大型项目,每个模块惰性加载可以提高性能,隐藏实现细节,暴露出常用的api。常见的类库比如underscore,jQuery我们都可以将其理解为单例模式的应用。

结合实战

前面已经讲过,单例模式是最常用的设计模式之一,我们来举个例子进行说明,
下面的代码主要实现一个简单的日期帮助类,通过单例模式实现:

基本的单例模式结构

var dateTimeHelper = {
      now: function () {
        return new Date();
      },
      format: function (date) {
        return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
      }
    }; 
console.log(dateTimeHelper.now());

这段代码通过对象字面量实现单例模式,使用的时候直接调用方法即可。

惰性加载实现单例模式

var dateTimeHelper = (function () {
      function init() {
        return {
          now: function () {
            return new Date();
          },
          format: function (date) {
            return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
          }
        }
      }
      var instance = null;
      return {
        getInstance: function () {
          if (!instance) {
            instance = init();
          }
          return instance;
        }
      }
    })(); 
console.log(dateTimeHelper.getInstance().now())

这就是惰性加载的单例模式。

下面再来看几个实例:
实现1: 最简单的对象字面量

var singleton = {

    attr : 1,

    method : function(){ return this.attr; }

  }



var t1 = singleton ;

var t2 = singleton ;

    那么很显然的, t1 === t2 。

    十分简单,并且非常使用,不足之处在于没有什么封装性,所有的属性方法都是暴露的。对于一些需要使用私有变量的情况就显得心有余而力不足了。当然在对于 this 的问题上也是有一定弊端的。

    实现2:构造函数内部判断

    其实和最初的JS实现有点类似,不过是将对是否已经存在该类的实例的判断放入构造函数内部。

function Construct(){

  // 确保只有单例

  if( Construct.unique !== undefined ){

    return Construct.unique; 

  }

  // 其他代码

  this.name = "NYF";

  this.age="24";

  Construct.unique = this;

}



var t1 = new Construct() ;

var t2 = new Construct() ;

    那么也有的, t1 === t2 。

    也是非常简单,无非就是提出一个属性来做判断,但是该方式也没有安全性,一旦我在外部修改了Construct的unique属性,那么单例模式也就被破坏了。 

    实现3 : 闭包方式   

    对于大着 灵活 牌子的JS来说,任何问题都能找到 n 种答案,只不过让我自己去掂量孰优孰劣而已,下面就简单的举几个使用闭包实现单例模式的方法,无非也就是将创建了的单例缓存而已。

var single = (function(){

  var unique;

  function Construct(){

    // ... 生成单例的构造函数的代码

  }



  unique = new Constuct();



  return unique;

})();

    只要 每次讲 var t1 = single; var t2 = single;即可。 与对象字面量方式类似。不过相对而言更安全一点,当然也不是绝对安全。

    如果希望会用调用 single() 方式来使用,那么也只需要将内部的 return 改为

return function(){

    return unique;

  }

    以上方式也可以使用 new 的方式来进行(形式主义的赶脚)。当然这边只是给了闭包的一种例子而已,也可以在 Construct 中判断单例是否存在 等等。 各种方式在各个不同情况做好选着即可。

总结

单例模式的好处在于对代码的组织作用,将相关的属性和方法封装在一个不会被多次实例化的对象中,让代码的维护和调试更加轻松。隐藏了实现细节,可以防止被错误修改,还防止了全局命名空间的污染。另外可以通过惰性加载提高性能,减少不必要的内存消耗。

Javascript 相关文章推荐
你所要知道JS(DHTML)中的一些技巧
Jan 09 Javascript
jquery中ajax学习笔记4
Oct 16 Javascript
JS实现简单路由器功能的方法
May 27 Javascript
JavaScript获取两个数组交集的方法
Jun 09 Javascript
简介JavaScript中的getSeconds()方法的使用
Jun 10 Javascript
简单谈谈JavaScript的同步与异步
Dec 31 Javascript
微信开发 使用picker封装省市区三级联动模板
Oct 28 Javascript
DropDownList实现可输入可选择(两种版本可选)
Dec 07 Javascript
JavaScript实现构造json数组的方法分析
Aug 17 Javascript
微信小程序实现跳转的几种方式总结(推荐)
Apr 24 Javascript
vue跳转同一个组件,参数不同,页面接收值只接收一次的解决方法
Nov 05 Javascript
如何将JavaScript将数组转为树形结构
Jun 02 Javascript
js数组常见操作及数组与字符串相互转化实例详解
Nov 10 #Javascript
浅谈javascript中replace()方法
Nov 10 #Javascript
使用jQuery获取data-的自定义属性
Nov 10 #Javascript
javascript适合移动端的日期时间拾取器
Nov 10 #Javascript
js图片轮播手动切换效果
Nov 10 #Javascript
JS截取与分割字符串常用技巧总结
Nov 10 #Javascript
jquery validate.js表单验证入门实例(附源码)
Nov 10 #Javascript
You might like
杏林同学录(一)
2006/10/09 PHP
joomla内置的表单验证功能使用方法
2010/06/11 PHP
php+mysqli实现批量替换数据库表前缀的方法
2014/12/29 PHP
PHP中$this和$that指针使用实例
2015/01/06 PHP
PHP接口并发测试的方法(推荐)
2016/12/15 PHP
PHP编程求最大公约数与最小公倍数的方法示例
2017/05/29 PHP
PHP获取数组中指定的一列实例
2017/12/27 PHP
php封装db类连接sqlite3数据库的方法实例
2017/12/19 PHP
ThinkPHP框架实现的MySQL数据库备份功能示例
2018/05/24 PHP
php实现数组重复数字统计实例
2018/09/30 PHP
利用js对象弹出一个层
2008/03/26 Javascript
网站导致浏览器崩溃的原因总结(多款浏览器) 推荐
2010/04/15 Javascript
js报$ is not a function 的问题的解决方法
2014/01/20 Javascript
jquery-tips悬浮提示插件分享
2015/07/31 Javascript
JavaScript动态设置div的样式的方法
2015/12/26 Javascript
JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法
2016/08/05 Javascript
Javascript单例模式的介绍和实例
2016/10/08 Javascript
判断数组的最佳方法(推荐)
2016/10/11 Javascript
老生常谈jquery id选择器和class选择器的区别
2017/02/12 Javascript
使用 Javascript 实现浏览器推送提醒功能的示例
2017/11/03 Javascript
Python使用爬虫猜密码
2016/02/19 Python
Python中在脚本中引用其他文件函数的实现方法
2016/06/23 Python
numpy 进行数组拼接,分别在行和列上合并的实例
2018/05/08 Python
Python读取数据集并消除数据中的空行方法
2018/07/12 Python
让你的Python代码实现类型提示功能
2019/11/19 Python
python实现爱奇艺登陆密码RSA加密的方法示例详解
2020/05/27 Python
pytorch中的weight-initilzation用法
2020/06/24 Python
HTML5中meta属性的使用方法
2016/02/29 HTML / CSS
Gweniss格温妮丝女包官网:英国纯手工制造潮流包包品牌
2018/02/07 全球购物
Under Armour安德玛荷兰官网:美国高端运动科技品牌
2019/07/10 全球购物
英国Lookfantastic中文网站:护肤品美妆美发购物(英国直邮)
2020/04/27 全球购物
调解协议书
2014/04/16 职场文书
关于安全的标语
2014/06/10 职场文书
反对四风问题自我剖析材料
2014/09/29 职场文书
2015年酒店服务员工作总结
2015/05/18 职场文书
python playwright 自动等待和断言详解
2021/11/27 Python