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

     首先来看看传统面向对象语言对于单体模式的定义:单体模式是只能被实例化一次并且可以通过一个众所周知的访问点来访问的类。这个定义有两点突出了传统面向对象语言的特征,即类和实例化,所以对于传统面向对象语言来讲,单体模式是建立在其类和实例化的自然特性之上的,即使用关键字class定义一个类,该类可通过new关键字来实例化,但是需要保证每次被new实例化之后得到的都是同一个实例或者说只能通过new来调用其构造函数一次。
 
     再来看看javascript中对于单体模式的定义:单体是一个用来划分命名空间并将一批相关方法和属性组织在一起的对象,如果它能够被实例化,那么只能被实例化一次。对比上面的定义,你会发现这里的单体定义将其实质定义为对象,而不是传统面向对象语言中的类,这也表明了javascript这门语言是基于对象的。同时后面又指出,如果能够被实例化,这说明了在javascript中单体定义应该有好几种方式,存在一种或几种能够被实例化即使用new关键字来创建单体对象的方式,但是这种方式不是javascript自身的自然特征,因为使用new关键字创造出来的对象,实际上都是通过function来模拟定义其构造函数的(虽然ES6开始支持class关键字了,但是目前还没有得到浏览器广泛支持),那么如何使用javascript的自然特征来实现单体模式呢?单体模式的基本结构如下:
  var Singleton = {
attr1: ,
attr2: ,
method1: function() {
alert(this.attr1); //将方法绑定到事件监听器上,this指向的dom元素就会失效,这里最好使用单体对象的全名访问属性和方法
},
method2: function(arg) {
}
};

绑定事件测试代码如下:

 <html>
<body>
<input id="btntest" type="button" value="测试" />
</body>
</html>
<script>
var elem = document.getElementById("btntest");
elem.addEventListener("click", Singleton.method1); //undefined
</script>
     基本结构中定义了一个对象Singleton,内部包含若干属性和方法,将其包含在页面中,js载入的时候就创建了这个对象,在调用时使用Singleton.method1来调用,它的实例化是随着页面载入js解析执行过程中完成的,我们并没有使用new关键字来实例化这个对象,这也是javascript中实现单体模式和传统面向对象语言一个很大的不同。这种方式更为简单易于理解。但是这种方式存在若干缺点,一个很明显的缺点是它并没有提供命名空间,其他程序员如果在页面中也定义了一个Singleton变量,那么很容易改写和混淆这个单体对象,于是针对这个问题,将其改写如下:
   var namespace={};
namespace.Singleton={
attr1:true,
attr2:10,
method1:function(){
},
method2:function(arg){
}
     这里首先定义了一个namespace的命名空间,然后将单体对象Singleton挂载在这个对象的下面,这大大减少了和其他程序员冲突以及误操作的可能,即使其他人在全局作用域中定义一个Singleton变量,也不会污染到这个单体对象,这就实现了前面定义中所说的划分命名空间并且将一些相关属性和方法组织在一起的功能。
     这个方法依然存在缺点,这个单体对象的所有属性和方法都是共有的,外部可随时访问和修改,于是采用闭包来模拟私有属性和方法,如下:
   namespace.Singleton = (function() {
var privateAttr1 = false;
var privateAttr2 = [, , ];
function privateMethod1() { }
function privateMethod2() { }
return {
publicAttr1: true,
publicAttr2: ,
publicMethod1: function() {
privateAttr1 = true;
privateMethod1();
},
publicMethod2: function(arg) {
privateAttr2 = [, , ];
privateMethod2();
}
}
})();
      在这里我们直接给该单体对象赋值了一个匿名自执行的函数,在该函数中使用var和function关键字分别来定义其私有属性和方法,这些在函数外部(单体对象外部)是无法直接访问的,因为函数一执行完毕,其内部作用域的空间就会被回收,这也就是能够利用闭包来模拟私有属性和方法的原因所在。在该函数(闭包)中,同时最终返回一个对象,这个对象中包含一些公有方法和属性,在外部可以直接调用,同时这些公有方法由于定义在函数内部,所以可以调用其私有属性和方法,但是外界只能通过返回的公有方法和属性来完成某些操作,不能够直接调用Singleton.privateMethod1这些属性。这就使得该单体对象既隔离了外界去直接访问其私有属性和方法,又提供给外界一些共有属性和方法去完成某些操作。
 
     这种匿名函数自执行所构造的单体模式在很多js库中被广泛使用,但是依然存在一个问题,如果我们在载入页面的时候并不需要用到该对象,而且该对象的创建比较耗费开销(如需要进行大量计算或需要多次访问dom树及其属性等)时,合理的做法是需要它的时候再去创建它,而不是随着js的解析执行直接去创建,这种概念被称之为惰性加载(lazy loading),于是修改以上代码如下:
     var Singleton = (function() {
var instantiated; //匿名函数创建私有变量,判断单体对象是否被创建的句柄
function init() {
return {
publicMethod: function() {
alert('hello word');
},
publicPrototype: "test"
};
}
return {
getinstance: function() {
if (!instantiated) {
instantiated = new init();
}
return instantiated;
}
}
})();
Singleton.getinstance().publicMethod();
     这里首先在匿名函数中定义了一个私有变量instantiated,作为一个判断单体对象是否被创建出来的句柄,然后将刚才所有对单体对象定义的属性和方法都放在一个名为init的函数中,只有该函数调用了,才会创造出该单体对象,否则不会直接创建它。然后,返回一个对象,其包含一个getInstance方法,该方法是供外部调用的,调用该方法的时候首先判断该单体对象是否存在,如果存在就直接返回它,否则调用init函数构造这个单体对象再返回它。最后如果我们调用该单体对象的某个方法,需要使用Singleton.getInstance().publicMethod(),这里,只有我们这样调用的时候才会创建这个单体对象,否则该单体对象是不会被自动创建的,这实际上就实现了按需加载或者惰性加载。

JS设计模式之单体模式(Singleton)的更多相关文章

  1. JS设计模式——5.单体模式

    JS设计模式——5.单体模式 http://www.cnblogs.com/JChen666/p/3610585.html   单体模式的优势 用了这么久的单体模式,竟全然不知!用它具体有哪些好处呢? ...

  2. JS设计模式——5.单体模式(用了这么久,竟全然不知!)

    单体模式的优势 用了这么久的单体模式,竟全然不知!用它具体有哪些好处呢? 1.可以用它来划分命名空间(这个就是就是经常用的了) 2.利用分支技术来封装浏览器之间的差异(这个还真没用过,挺新鲜) 3.借 ...

  3. 单例/单体模式(Singleton)

    单例/单体模式(Singleton) 首先,单例模式是对象的创建模式之一,此外还包括工厂模式. 单例模式的三个特点: 1,该类只有一个实例 2,该类自行创建该实例(在该类内部创建自身的实例对象) 3, ...

  4. js设计模式——7.备忘录模式

    js设计模式——7.备忘录模式 /*js设计模式——备忘录模式*/ // 备忘类 class Memento { constructor(content) { this.content = conte ...

  5. js设计模式——6.模板方法模式与职责链模式

    js设计模式——6.模板方法模式与职责链模式 职责链模式

  6. js设计模式——5.状态模式

    js设计模式——5.状态模式 代码演示 /*js设计模式——状态模式*/ // 状态(红灯,黄灯,绿灯) class State { constructor(color) { this.color = ...

  7. js设计模式——4.迭代器模式

    js设计模式——4.迭代器模式 代码演示 /*js设计模式——迭代器模式*/ class Iterator { constructor(container) { this.list = contain ...

  8. js设计模式——2.外观模式

    js设计模式——2.外观模式

  9. js设计模式——1.代理模式

    js设计模式——1.代理模式 以下是代码示例 /*js设计模式——代理模式*/ class ReadImg { constructor(fileName) { this.fileName = file ...

随机推荐

  1. Python 使用Pandas读取Excel的学习笔记

    这里介绍Python中使用Pandas读取Excel的方法 一.软件环境: OS:Win7 64位 Python 3.7 二.文件准备 1.项目结构: 2.在当前实验文件夹下建立一个Source文件夹 ...

  2. How To Check Member In Window VS With CplusPlus?

    实例说明 下面这个实例代码, 快速举例了在Win32应用程序下,对于内存的泄漏检查. 其中的原理,目前本人还是不太的理解. 只是记录了使用方法. 以后,看情况,会更新的. #ifdef _WIN32 ...

  3. Eclipse下生成.dll动态库及.a静态库使用 for Windows [z]

    以后的主要工作就是做库了,将我们的C或者C++写的接口做成库,给客户端使用,因此有必要知道库的使用和制作方法.主要是在Eclipse下搞了搞,公司用的是Carbide,也差不多.库做好了,用SVN已提 ...

  4. loadrunner12: Error -27492: "HttpSendRequest" failed, Windows error code=8

    这个问题我在网上看到有这样的解释:1.timeout时间超时设置问题2.Run-Time Settings -> Preferences -> Advanced. 确定此选项未被选中:&q ...

  5. git查看远程仓库地址

    git remote -v

  6. http://www.atool.org/keytype.php#0-tsina-1-53371-397232819ff9a47a7b7e80a40613cfe1

    http://www.atool.org/keytype.php#0-tsina-1-53371-397232819ff9a47a7b7e80a40613cfe1

  7. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果

    目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果 ...

  8. 团体程序设计天梯赛L1-020 帅到没朋友 2017-03-22 17:46 72人阅读 评论(0) 收藏

    L1-020. 帅到没朋友 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 当芸芸众生忙着在朋友圈中发照片的时候,总有一些人因为 ...

  9. java性能分析工具 jconsole.exe

    通过 Java visualMv结合 jconsole.exe   工具即可查看如图所示(Jconsole在JDK文件夹内,非JRE文件夹) 在Java Visualvm工具里面安装JTA插件,分析线 ...

  10. 基于S2SH开发病房管理系统的设计与实现 源码

    基于S2SH开发病房管理系统的设计与实现: 开发环境: Windows操作系统 开发工具:Eclipse/MyEclipse+Jdk+Tomcat+MySQL数据库 运行效果图:       此源码经 ...