JavaScript中实现单体模式分享


Posted in Javascript onJanuary 29, 2015

单体模式作为一种软件开发模式在众多面向对象语言中得到了广泛的使用,在javascript中,单体模式也是使用非常广泛的,但是由于javascript语言拥有其独特的面向对象方式,导致其和一些传统面向对象语言虽然在单体模式的思想上是一致的,但是实现起来还是有差异的。

首先来看看传统面向对象语言对于单体模式的定义:单体模式是只能被实例化一次并且可以通过一个众所周知的访问点来访问的类。这个定义有两点突出了传统面向对象语言的特征,即类和实例化,所以对于传统面向对象语言来讲,单体模式是建立在其类和实例化的自然特性之上的,即使用关键字class定义一个类,该类可通过new关键字来实例化,但是需要保证每次被new实例化之后得到的都是同一个实例或者说只能通过new来调用其构造函数一次。

再来看看javascript中对于单体模式的定义:单体是一个用来划分命名空间并将一批相关方法和属性组织在一起的对象,如果它能够被实例化,那么只能被实例化一次。对比上面的定义,你会发现这里的单体定义将其实质定义为对象,而不是传统面向对象语言中的类,这也表明了javascript这门语言是基于对象的。同时后面又指出,如果能够被实例化,这说明了在javascript中单体定义应该有好几种方式,存在一种或几种能够被实例化即使用new关键字来创建单体对象的方式,但是这种方式不是javascript自身的自然特征,因为使用new关键字创造出来的对象,实际上都是通过function来模拟定义其构造函数的(虽然ES6开始支持class关键字了,但是目前还没有得到浏览器广泛支持),那么如何使用javascript的自然特征来实现单体模式呢?

var Singleton={
  attribute1:true,
  attribute2:10,
  method1:function(){

  },
  method2:function(arg){

  }
}

这里定义了一个对象Singleton,内部包含若干属性和方法,将其包含在页面中,js载入的时候就创建了这个对象,在调用时使用Singleton.method1来调用,它的实例化是随着页面载入js解析执行过程中完成的,我们并没有使用new关键字来实例化这个对象,这也是javascript中实现单体模式和传统面向对象语言一个很大的不同。这种方式更为简单易于理解。但是这种方式存在若干缺点,一个很明显的缺点是它并没有提供命名空间,其他程序员如果在页面中也定义了一个Singleton变量,那么很容易改写和混淆这个单体对象,于是针对这个问题,将其改写如下:

var mySpace={};
mySpace.Singleton={
  attribute1:true,
  attribute2:10,
  method1:function(){

  },
  method2:function(arg){

  }
}

这里首先定义了一个mySpace的命名空间,然后将单体对象Singleton挂载在这个对象的下面,这大大减少了和其他程序员冲突以及误操作的可能,即使其他人在全局作用域中定义一个Singleton变量,也不会污染到这个单体对象,这就实现了前面定义中所说的划分命名空间并且将一些相关属性和方法组织在一起的功能。

这个方法依然存在缺点,这个单体对象的所有属性和方法都是共有的,外部可随时访问和修改,于是采用闭包来模拟私有属性和方法,如下:

mySpace.Singleton=(function(){
  var privateAttribute1=false;
  var privateAttribute1=[1,2,3];
  function privateMethod1(){

  }
  function privateMethod2(){

  }

  return {
  publicAttribute1:true,
  publicAttribute2:10,
  publicMethod1:function(){
    privateAttribute1=true;
    privateMethod1();
  },
  publicMethod2:function(arg){
    privateAttribute1=[4,5,6];
    privateMethod2();
  }

  }

})();

在这里我们直接给该单体对象赋值了一个匿名自执行的函数,在该函数中使用var和function关键字分别来定义其私有属性和方法,这些在函数外部(单体对象外部)是无法直接访问的,因为函数一执行完毕,其内部作用域的空间就会被回收,这也就是能够利用闭包来模拟私有属性和方法的原因所在。在该函数(闭包)中,同时最终返回一个对象,这个对象中包含一些公有方法和属性,在外部可以直接调用,同时这些公有方法由于定义在函数内部,所以可以调用其私有属性和方法,但是外界只能通过返回的公有方法和属性来完成某些操作,不能够直接调用Singleton.privateMethod1这些属性。这就使得该单体对象既隔离了外界去直接访问其私有属性和方法,又提供给外界一些共有属性和方法去完成某些操作。

这种匿名函数自执行所构造的单体模式在很多js库中被广泛使用,但是依然存在一个问题,如果我们在载入页面的时候并不需要用到该对象,而且该对象的创建比较耗费开销(如需要进行大量计算或需要多次访问dom树及其属性等)时,合理的做法是需要它的时候再去创建它,而不是随着js的解析执行直接去创建,这种概念被称之为惰性加载(lazy loading),于是修改以上代码如下:

mySpace.Singleton=(function(){
    var uniqueInstance;
    function constructor(){
      var privateAttribute1=false;
      var privateAttribute1=[1,2,3];
      function privateMethod1(){
      }
      function privateMethod2(){
      }
      return {
        publicAttribute1:true,
        publicAttribute2:10,
        publicMethod1:function(){
          privateAttribute1=true;
          privateMethod1();
        },
        publicMethod2:function(arg){
          privateAttribute1=[4,5,6];
          privateMethod2();
        }

      }
    }

    return {
      getInstance:function(){
       if(!uniqueInstance){
         uniqueInstance=constructor();
       }
        return uniqueInstance;
      }
    }

  })();

这里首先在匿名函数中定义了一个私有变量uniqueInstance,作为一个判断单体对象是否被创建出来的句柄,然后将刚才所有对单体对象定义的属性和方法都放在一个名为constructor的函数中,只有该函数调用了,才会创造出该单体对象,否则不会直接创建它。然后,返回一个对象,其包含一个getInstance方法,该方法是供外部调用的,调用该方法的时候首先判断该单体对象是否存在,如果存在就直接返回它,否则调用constructor函数构造这个单体对象再返回它。最后如果我们调用该单体对象的某个方法,需要使用mySpace.Singleton.getInstance().publicMethod1(),这里,只有我们这样调用的时候才会创建这个单体对象,否则该单体对象是不会被自动创建的,这实际上就实现了按需加载或者惰性加载。

Javascript 相关文章推荐
查看源码的工具 学习jQuery源码不错的工具
Dec 26 Javascript
js图片处理示例代码
May 12 Javascript
table insertRow、deleteRow定义和用法总结
May 14 Javascript
原生javascript实现隔行换色
Jan 04 Javascript
JavaScript中三种异步上传文件方式
Mar 06 Javascript
一个简单的JavaScript Map实例(分享)
Aug 03 Javascript
原生JS:Date对象全面解析
Sep 06 Javascript
详解自动生成博客目录案例
Dec 09 Javascript
详解Javascript获取缓存和清除缓存API
May 25 Javascript
基于vue-resource jsonp跨域问题的解决方法
Feb 03 Javascript
JS实现的合并多个数组去重算法示例
Apr 11 Javascript
VsCode与Node.js知识点详解
Sep 05 Javascript
angular简介和其特点介绍
Jan 29 #Javascript
javascript实现获取浏览器版本、操作系统类型
Jan 29 #Javascript
浅谈javascript中自定义模版
Jan 29 #Javascript
jQuery和AngularJS的区别浅析
Jan 29 #Javascript
node.js中的forEach()是同步还是异步呢
Jan 29 #Javascript
Node.js事件循环(Event Loop)和线程池详解
Jan 28 #Javascript
使用Sticker.js实现贴纸效果
Jan 28 #Javascript
You might like
Php Image Resize图片大小调整的函数代码
2011/01/17 PHP
Session保存到数据库的php类分享
2011/10/24 PHP
golang与php实现计算两个经纬度之间距离的方法
2016/07/22 PHP
linux平台编译安装PHP7并安装Redis扩展与Swoole扩展实例教程
2016/09/30 PHP
默认让页面的第一个控件选中的javascript代码
2009/12/26 Javascript
DIV外区域Click后关闭DIV的实现代码
2011/12/21 Javascript
去掉gridPanel表头全选框的小例子
2013/07/18 Javascript
javascript不同类型数据之间的运算的转换方法
2014/02/13 Javascript
jQuery实现鼠标滚轮动态改变样式或效果
2015/01/05 Javascript
jQuery实现带水平滑杆的焦点图动画插件
2016/03/08 Javascript
基于javascript实现九九乘法表
2016/03/27 Javascript
纯JS前端实现分页代码
2016/06/21 Javascript
详解基于Koa2开发微信二维码扫码支付相关流程
2018/05/16 Javascript
菊花转动的jquery加载动画效果
2018/08/19 jQuery
对Vue.js之事件的绑定(v-on: 或者 @ )详解
2018/09/15 Javascript
Vue 中获取当前时间并实时刷新的实现代码
2020/05/12 Javascript
详解JavaScript 事件流
2020/09/02 Javascript
Python+Django搭建自己的blog网站
2018/03/13 Python
python安装模块如何通过setup.py安装(超简单)
2018/05/05 Python
Python for循环中的陷阱详解
2018/07/13 Python
python如何创建TCP服务端和客户端
2018/08/26 Python
python logging日志模块原理及操作解析
2019/10/12 Python
python函数局部变量、全局变量、递归知识点总结
2019/11/15 Python
PyQt5 如何让界面和逻辑分离的方法
2020/03/24 Python
python对execl 处理操作代码
2020/06/22 Python
Python+unittest+requests 接口自动化测试框架搭建教程
2020/10/09 Python
python实现excel公式格式化的示例代码
2020/12/23 Python
Parfume Klik丹麦:香水网上商店
2018/07/10 全球购物
机关干部三严三实心得体会
2014/10/13 职场文书
1000字打架检讨书
2014/11/03 职场文书
2014年化验员工作总结
2014/11/18 职场文书
先进班组材料范文
2014/12/25 职场文书
防溺水安全教育主题班会
2015/08/12 职场文书
python爬取某网站原图作为壁纸
2021/06/02 Python
Python3 多线程(连接池)操作MySQL插入数据
2021/06/09 Python
springboot拦截器无法注入redisTemplate的解决方法
2021/06/27 Java/Android