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 相关文章推荐
javascript 复杂的嵌套环境中输出单引号和双引号
May 26 Javascript
JavaScript的变量作用域深入理解
Oct 25 Javascript
用Greasemonkey 脚本收藏网站会员信息到本地
Oct 26 Javascript
动态标签 悬停效果 延迟加载示例代码
Nov 21 Javascript
vue.js入门教程之计算属性
Sep 01 Javascript
js 轮播效果实例分享
Dec 28 Javascript
使用snowfall.jquery.js实现爱心满屏飞的效果
Jan 05 Javascript
详谈表单格式化插件jquery.serializeJSON
Jun 23 jQuery
Vue 中使用vue2-highcharts实现top功能的示例
Mar 05 Javascript
微信小程序框架wepy之动态控制类名
Sep 14 Javascript
基于vue实现一个神奇的动态按钮效果
May 15 Javascript
关于layui表单中按钮自动提交的解决方法
Sep 09 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在项目中寻找代码的坏味道(综艺命名)
2012/07/19 PHP
ThinkPHP基于PHPExcel导入Excel文件的方法
2014/10/15 PHP
php实现读取手机客户端浏览器的类
2015/01/09 PHP
PHP SPL标准库之接口(Interface)详解
2015/05/11 PHP
php获取从百度、谷歌等搜索引擎进入网站关键词的方法
2015/07/08 PHP
php cli配置文件问题分析
2015/10/15 PHP
Thinkphp微信公众号支付接口
2016/08/04 PHP
Javascript客户端将指定区域导出到Word、Excel的代码
2008/10/22 Javascript
javascript跨域刷新实现代码
2011/01/01 Javascript
JavaScript中几种常见排序算法小结
2011/02/22 Javascript
js实现连个数字相加而不是拼接的方法
2014/02/23 Javascript
Javascript获取图片原始宽度和高度的方法详解
2016/09/20 Javascript
JS前向后瞻正则表达式定义与用法示例
2016/12/27 Javascript
微信小程序 九宫格实例代码
2017/01/21 Javascript
深入理解JavaScript中的尾调用(Tail Call)
2017/02/07 Javascript
网页中的图片查看器viewjs使用方法
2017/07/11 Javascript
mui开发中获取单选按钮、复选框的值(实例讲解)
2017/07/24 Javascript
Redux 和 Mobx的选择问题:让你不再困惑!
2017/09/18 Javascript
Vuejs在v-for中,利用index来对第一项添加class的方法
2018/03/03 Javascript
js实现敏感词过滤算法及实现逻辑
2018/07/24 Javascript
vue-cli 关闭热更新操作
2020/09/18 Javascript
vue内置组件keep-alive事件动态缓存实例
2020/10/30 Javascript
python导出hive数据表的schema实例代码
2018/01/22 Python
在Python中表示一个对象的方法
2019/06/25 Python
python自定义时钟类、定时任务类
2021/02/22 Python
Python实现寻找回文数字过程解析
2020/06/09 Python
Python try except finally资源回收的实现
2021/01/25 Python
HTML table 表格边框的实现思路
2019/10/12 HTML / CSS
运动会四百米广播稿
2014/01/19 职场文书
租房协议书样本
2014/08/20 职场文书
公安局副政委班子个人对照检查材料
2014/10/04 职场文书
群众路线自我剖析材料
2014/10/08 职场文书
2014年英语教学工作总结
2014/12/17 职场文书
六一文艺汇演开幕词
2015/01/29 职场文书
青年岗位能手事迹材料(2016推荐版)
2016/03/01 职场文书
mysql数据插入覆盖和时间戳的问题及解决
2022/03/25 MySQL